blob: e48a4a1bfea7229aa88777ff67b5ed7c32b29fad [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All 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 <algorithm>
#include <utility>
#include <vector>
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/sparse_histogram.h"
#include "base/metrics/user_metrics.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/form_data.h"
#include "services/metrics/public/cpp/ukm_entry_builder.h"
namespace internal {
const char kUKMCardUploadDecisionEntryName[] = "Autofill.CardUploadDecision";
const char kUKMCardUploadDecisionMetricName[] = "UploadDecision";
const char kUKMDeveloperEngagementEntryName[] = "Autofill.DeveloperEngagement";
const char kUKMDeveloperEngagementMetricName[] = "DeveloperEngagement";
const char kUKMMillisecondsSinceFormParsedMetricName[] =
"MillisecondsSinceFormParsed";
const char kUKMInteractedWithFormEntryName[] = "Autofill.InteractedWithForm";
const char kUKMIsForCreditCardMetricName[] = "IsForCreditCard";
const char kUKMLocalRecordTypeCountMetricName[] = "LocalRecordTypeCount";
const char kUKMServerRecordTypeCountMetricName[] = "ServerRecordTypeCount";
const char kUKMSuggestionsShownEntryName[] = "Autofill.SuggestionsShown";
const char kUKMSelectedMaskedServerCardEntryName[] =
"Autofill.SelectedMaskedServerCard";
const char kUKMSuggestionFilledEntryName[] = "Autofill.SuggestionFilled";
const char kUKMRecordTypeMetricName[] = "RecordType";
const char kUKMTextFieldDidChangeEntryName[] = "Autofill.TextFieldDidChange";
const char kUKMFieldTypeGroupMetricName[] = "FieldTypeGroup";
const char kUKMHeuristicTypeMetricName[] = "HeuristicType";
const char kUKMServerTypeMetricName[] = "ServerType";
const char kUKMHtmlFieldTypeMetricName[] = "HtmlFieldType";
const char kUKMHtmlFieldModeMetricName[] = "HtmlFieldMode";
const char kUKMIsAutofilledMetricName[] = "IsAutofilled";
const char kUKMIsEmptyMetricName[] = "IsEmpty";
const char kUKMFormSubmittedEntryName[] = "Autofill.AutofillFormSubmitted";
const char kUKMAutofillFormSubmittedStateMetricName[] =
"AutofillFormSubmittedState";
// |UkmEntry| for capturing field type prediction quality.
const char kUKMFieldTypeEntryName[] = "Autofill.FieldTypeValidation";
const char kUKMFieldFillStatusEntryName[] = "Autofill.FieldFillStatus";
const char kUKMFormSignatureMetricName[] = "FormSignature";
const char kUKMFieldSignatureMetricName[] = "FieldSignature";
const char kUKMValidationEventMetricName[] = "ValidationEvent";
const char kUKMPredictionSourceMetricName[] = "PredictionSource";
const char kUKMPredictedTypeMetricName[] = "PredictedType";
const char kUKMActualTypeMetricName[] = "ActualType";
const char kUKMWasSuggestionShownMetricName[] = "WasSuggestionShown";
const char kUKMWasPreviouslyAutofilledMetricName[] = "WasPreviouslyAutofilled";
} // namespace internal
namespace autofill {
namespace {
// Note: if adding an enum value here, update the corresponding description for
// AutofillTypeQualityByFieldType in histograms.xml.
enum FieldTypeGroupForMetrics {
GROUP_AMBIGUOUS = 0,
GROUP_NAME,
GROUP_COMPANY,
GROUP_ADDRESS_LINE_1,
GROUP_ADDRESS_LINE_2,
GROUP_ADDRESS_CITY,
GROUP_ADDRESS_STATE,
GROUP_ADDRESS_ZIP,
GROUP_ADDRESS_COUNTRY,
GROUP_PHONE,
GROUP_FAX, // Deprecated.
GROUP_EMAIL,
GROUP_CREDIT_CARD_NAME,
GROUP_CREDIT_CARD_NUMBER,
GROUP_CREDIT_CARD_DATE,
GROUP_CREDIT_CARD_TYPE,
GROUP_PASSWORD,
GROUP_ADDRESS_LINE_3,
GROUP_USERNAME,
GROUP_STREET_ADDRESS,
GROUP_CREDIT_CARD_VERIFICATION,
NUM_FIELD_TYPE_GROUPS_FOR_METRICS
};
const int KMaxFieldTypeGroupMetric =
(NUM_FIELD_TYPE_GROUPS_FOR_METRICS << 8) |
AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS;
std::string PreviousSaveCreditCardPromptUserDecisionToString(
int previous_save_credit_card_prompt_user_decision) {
DCHECK_LT(previous_save_credit_card_prompt_user_decision,
prefs::NUM_PREVIOUS_SAVE_CREDIT_CARD_PROMPT_USER_DECISIONS);
std::string previous_response;
if (previous_save_credit_card_prompt_user_decision ==
prefs::PREVIOUS_SAVE_CREDIT_CARD_PROMPT_USER_DECISION_ACCEPTED)
previous_response = ".PreviouslyAccepted";
else if (previous_save_credit_card_prompt_user_decision ==
prefs::PREVIOUS_SAVE_CREDIT_CARD_PROMPT_USER_DECISION_DENIED)
previous_response = ".PreviouslyDenied";
else
DCHECK_EQ(previous_save_credit_card_prompt_user_decision,
prefs::PREVIOUS_SAVE_CREDIT_CARD_PROMPT_USER_DECISION_NONE);
return previous_response;
}
} // namespace
// 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.
// NOTE: This is defined outside of the anonymous namespace so that it can be
// accessed from the unit test file. It is not exposed in the header file,
// however, because it is not intended for consumption outside of the metrics
// implementation.
int GetFieldTypeGroupMetric(ServerFieldType field_type,
AutofillMetrics::FieldTypeQualityMetric metric) {
DCHECK_LT(metric, AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
FieldTypeGroupForMetrics group = GROUP_AMBIGUOUS;
switch (AutofillType(field_type).group()) {
case NO_GROUP:
group = GROUP_AMBIGUOUS;
break;
case NAME:
case NAME_BILLING:
group = GROUP_NAME;
break;
case COMPANY:
group = GROUP_COMPANY;
break;
case ADDRESS_HOME:
case ADDRESS_BILLING:
switch (AutofillType(field_type).GetStorableType()) {
case ADDRESS_HOME_LINE1:
group = GROUP_ADDRESS_LINE_1;
break;
case ADDRESS_HOME_LINE2:
group = GROUP_ADDRESS_LINE_2;
break;
case ADDRESS_HOME_LINE3:
group = GROUP_ADDRESS_LINE_3;
break;
case ADDRESS_HOME_STREET_ADDRESS:
group = GROUP_STREET_ADDRESS;
case ADDRESS_HOME_CITY:
group = GROUP_ADDRESS_CITY;
break;
case ADDRESS_HOME_STATE:
group = GROUP_ADDRESS_STATE;
break;
case ADDRESS_HOME_ZIP:
group = GROUP_ADDRESS_ZIP;
break;
case ADDRESS_HOME_COUNTRY:
group = GROUP_ADDRESS_COUNTRY;
break;
default:
NOTREACHED() << field_type << " has no group assigned (ambiguous)";
group = GROUP_AMBIGUOUS;
break;
}
break;
case EMAIL:
group = GROUP_EMAIL;
break;
case PHONE_HOME:
case PHONE_BILLING:
group = GROUP_PHONE;
break;
case CREDIT_CARD:
switch (field_type) {
case CREDIT_CARD_NAME_FULL:
case CREDIT_CARD_NAME_FIRST:
case CREDIT_CARD_NAME_LAST:
group = GROUP_CREDIT_CARD_NAME;
break;
case CREDIT_CARD_NUMBER:
group = GROUP_CREDIT_CARD_NUMBER;
break;
case CREDIT_CARD_TYPE:
group = GROUP_CREDIT_CARD_TYPE;
break;
case CREDIT_CARD_EXP_MONTH:
case CREDIT_CARD_EXP_2_DIGIT_YEAR:
case CREDIT_CARD_EXP_4_DIGIT_YEAR:
case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR:
group = GROUP_CREDIT_CARD_DATE;
break;
case CREDIT_CARD_VERIFICATION_CODE:
group = GROUP_CREDIT_CARD_VERIFICATION;
break;
default:
NOTREACHED() << field_type << " has no group assigned (ambiguous)";
group = GROUP_AMBIGUOUS;
break;
}
break;
case PASSWORD_FIELD:
group = GROUP_PASSWORD;
break;
case USERNAME_FIELD:
group = GROUP_USERNAME;
break;
case TRANSACTION:
NOTREACHED();
break;
}
// Use bits 8-15 for the group and bits 0-7 for the metric.
static_assert(AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS <= UINT8_MAX,
"maximum field type quality metric must fit into 8 bits");
static_assert(NUM_FIELD_TYPE_GROUPS_FOR_METRICS <= UINT8_MAX,
"number of field type groups must fit into 8 bits");
return (group << 8) | metric;
}
namespace {
// 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_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);
}
const char* GetQualityMetricPredictionSource(
AutofillMetrics::QualityMetricPredictionSource source) {
switch (source) {
default:
case AutofillMetrics::PREDICTION_SOURCE_UNKNOWN:
NOTREACHED();
return "Unknown";
case AutofillMetrics::PREDICTION_SOURCE_HEURISTIC:
return "Heuristic";
case AutofillMetrics::PREDICTION_SOURCE_SERVER:
return "Server";
case AutofillMetrics::PREDICTION_SOURCE_OVERALL:
return "Overall";
}
}
const char* GetQualityMetricTypeSuffix(
AutofillMetrics::QualityMetricType metric_type) {
switch (metric_type) {
default:
NOTREACHED();
// Fall through...
case AutofillMetrics::TYPE_SUBMISSION:
return "";
case AutofillMetrics::TYPE_NO_SUBMISSION:
return ".NoSubmission";
case AutofillMetrics::TYPE_AUTOCOMPLETE_BASED:
return ".BasedOnAutocomplete";
}
}
// Given a set of |possible_types| for a field, select the best type to use as
// the "actual" field type when calculating metrics. If the |predicted_type| is
// among the |possible_types] then use that as the best type (i.e., the
// prediction is deemed to have been correct).
ServerFieldType GetActualFieldType(const ServerFieldTypeSet& possible_types,
ServerFieldType predicted_type) {
DCHECK_NE(possible_types.size(), 0u);
if (possible_types.count(EMPTY_TYPE)) {
DCHECK_EQ(possible_types.size(), 1u);
return EMPTY_TYPE;
}
if (possible_types.count(UNKNOWN_TYPE)) {
DCHECK_EQ(possible_types.size(), 1u);
return UNKNOWN_TYPE;
}
if (possible_types.count(predicted_type))
return predicted_type;
// Collapse field types that Chrome treats as identical, e.g. home and
// billing address fields.
ServerFieldTypeSet collapsed_field_types;
for (const auto& type : possible_types) {
DCHECK_NE(type, EMPTY_TYPE);
DCHECK_NE(type, UNKNOWN_TYPE);
// A phone number that's only missing its country code is (for metrics
// purposes) the same as the whole phone number.
if (type == PHONE_HOME_CITY_AND_NUMBER)
collapsed_field_types.insert(PHONE_HOME_WHOLE_NUMBER);
else
collapsed_field_types.insert(AutofillType(type).GetStorableType());
}
// Capture the field's type, if it is unambiguous.
ServerFieldType actual_type = AMBIGUOUS_TYPE;
if (collapsed_field_types.size() == 1)
actual_type = *collapsed_field_types.begin();
DVLOG(2) << "Inferred Type: " << AutofillType(actual_type).ToString();
return actual_type;
}
// Logs field type prediction quality metrics. The primary histogram name is
// constructed based on |prediction_source| The field-specific histogram names
// also incorporates the possible and predicted types for |field|. A suffix may
// be appended to the metric name, depending on |metric_type|.
void LogPredictionQualityMetrics(
AutofillMetrics::QualityMetricPredictionSource prediction_source,
ServerFieldType predicted_type,
AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger,
const FormStructure& form,
const AutofillField& field,
AutofillMetrics::QualityMetricType metric_type) {
// Generate histogram names.
const char* source = GetQualityMetricPredictionSource(prediction_source);
const char* suffix = GetQualityMetricTypeSuffix(metric_type);
std::string raw_data_histogram =
base::JoinString({"Autofill.FieldPrediction.", source, suffix}, "");
std::string aggregate_histogram = base::JoinString(
{"Autofill.FieldPredictionQuality.Aggregate.", source, suffix}, "");
std::string type_specific_histogram = base::JoinString(
{"Autofill.FieldPredictionQuality.ByFieldType.", source, suffix}, "");
const ServerFieldTypeSet& possible_types =
metric_type == AutofillMetrics::TYPE_AUTOCOMPLETE_BASED
? ServerFieldTypeSet{AutofillType(field.html_type(),
field.html_mode())
.GetStorableType()}
: field.possible_types();
// Get the best type classification we can for the field.
ServerFieldType actual_type =
GetActualFieldType(possible_types, predicted_type);
DVLOG(2) << "Predicted: " << AutofillType(predicted_type).ToString() << "; "
<< "Actual: " << AutofillType(actual_type).ToString();
DCHECK_LE(predicted_type, UINT16_MAX);
DCHECK_LE(actual_type, UINT16_MAX);
UMA_HISTOGRAM_SPARSE_SLOWLY(raw_data_histogram,
(predicted_type << 16) | actual_type);
form_interactions_ukm_logger->LogFieldType(
form.form_signature(), field.GetFieldSignature(), prediction_source,
metric_type, predicted_type, actual_type);
// NO_SERVER_DATA is the equivalent of predicting UNKNOWN.
if (predicted_type == NO_SERVER_DATA)
predicted_type = UNKNOWN_TYPE;
// The actual type being EMPTY_TYPE is the same as UNKNOWN_TYPE for comparison
// purposes, but remember whether or not it was empty for more precise logging
// later.
bool is_empty = (actual_type == EMPTY_TYPE);
bool is_ambiguous = (actual_type == AMBIGUOUS_TYPE);
if (is_empty || is_ambiguous)
actual_type = UNKNOWN_TYPE;
// If the predicted and actual types match then it's either a true positive
// or a true negative (if they are both unknown). Do not log type specific
// true negatives (instead log a true positive for the "Ambiguous" type).
if (predicted_type == actual_type) {
if (actual_type == UNKNOWN_TYPE) {
// Only log aggregate true negative; do not log type specific metrics
// for UNKNOWN/EMPTY.
DVLOG(2) << "TRUE NEGATIVE";
LogUMAHistogramEnumeration(
aggregate_histogram,
(is_empty ? AutofillMetrics::TRUE_NEGATIVE_EMPTY
: (is_ambiguous ? AutofillMetrics::TRUE_NEGATIVE_AMBIGUOUS
: AutofillMetrics::TRUE_NEGATIVE_UNKNOWN)),
AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
return;
}
DVLOG(2) << "TRUE POSITIVE";
// Log both aggregate and type specific true positive if we correctly
// predict that type with which the field was filled.
LogUMAHistogramEnumeration(aggregate_histogram,
AutofillMetrics::TRUE_POSITIVE,
AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
LogUMAHistogramEnumeration(
type_specific_histogram,
GetFieldTypeGroupMetric(actual_type, AutofillMetrics::TRUE_POSITIVE),
KMaxFieldTypeGroupMetric);
return;
}
// Note: At this point predicted_type != actual type
// If actual type is UNKNOWN_TYPE then the prediction is a false positive.
// Further specialize the type of false positive by whether the field was
// empty or contained an unknown value.
if (actual_type == UNKNOWN_TYPE) {
DVLOG(2) << "FALSE POSITIVE";
auto metric =
(is_empty ? AutofillMetrics::FALSE_POSITIVE_EMPTY
: (is_ambiguous ? AutofillMetrics::FALSE_POSITIVE_AMBIGUOUS
: AutofillMetrics::FALSE_POSITIVE_UNKNOWN));
LogUMAHistogramEnumeration(aggregate_histogram, metric,
AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
LogUMAHistogramEnumeration(type_specific_histogram,
GetFieldTypeGroupMetric(predicted_type, metric),
KMaxFieldTypeGroupMetric);
return;
}
// Note: At this point predicted_type != actual type, actual_type != UNKNOWN.
// If predicted type is UNKNOWN_TYPE then the prediction is a false negative
// unknown.
if (predicted_type == UNKNOWN_TYPE) {
DVLOG(2) << "FALSE NEGATIVE";
LogUMAHistogramEnumeration(aggregate_histogram,
AutofillMetrics::FALSE_NEGATIVE_UNKNOWN,
AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
LogUMAHistogramEnumeration(
type_specific_histogram,
GetFieldTypeGroupMetric(actual_type,
AutofillMetrics::FALSE_NEGATIVE_UNKNOWN),
KMaxFieldTypeGroupMetric);
return;
}
DVLOG(2) << "MISMATCH";
// Note: At this point predicted_type != actual type, actual_type != UNKNOWN,
// predicted_type != UNKNOWN.
// This is a mismatch. From the reference of the actual type, this is a false
// negative (it was T, but predicted U). From the reference of the prediction,
// this is a false positive (predicted it was T, but it was U).
LogUMAHistogramEnumeration(aggregate_histogram,
AutofillMetrics::FALSE_NEGATIVE_MISMATCH,
AutofillMetrics::NUM_FIELD_TYPE_QUALITY_METRICS);
LogUMAHistogramEnumeration(
type_specific_histogram,
GetFieldTypeGroupMetric(actual_type,
AutofillMetrics::FALSE_NEGATIVE_MISMATCH),
KMaxFieldTypeGroupMetric);
LogUMAHistogramEnumeration(
type_specific_histogram,
GetFieldTypeGroupMetric(predicted_type,
AutofillMetrics::FALSE_POSITIVE_MISMATCH),
KMaxFieldTypeGroupMetric);
}
} // namespace
// static
void AutofillMetrics::LogCardUploadDecisionMetrics(
int upload_decision_metrics) {
DCHECK(upload_decision_metrics);
DCHECK_LT(upload_decision_metrics, 1 << kNumCardUploadDecisionMetrics);
for (int metric = 0; metric < kNumCardUploadDecisionMetrics; ++metric)
if (upload_decision_metrics & (1 << metric))
UMA_HISTOGRAM_ENUMERATION("Autofill.CardUploadDecisionMetric", metric,
kNumCardUploadDecisionMetrics);
}
// static
void AutofillMetrics::LogCreditCardInfoBarMetric(
InfoBarMetric metric,
bool is_uploading,
int previous_save_credit_card_prompt_user_decision) {
DCHECK_LT(metric, NUM_INFO_BAR_METRICS);
std::string destination = is_uploading ? ".Server" : ".Local";
LogUMAHistogramEnumeration(
"Autofill.CreditCardInfoBar" + destination +
PreviousSaveCreditCardPromptUserDecisionToString(
previous_save_credit_card_prompt_user_decision),
metric, NUM_INFO_BAR_METRICS);
}
// static
void AutofillMetrics::LogCreditCardFillingInfoBarMetric(InfoBarMetric metric) {
DCHECK_LT(metric, NUM_INFO_BAR_METRICS);
UMA_HISTOGRAM_ENUMERATION("Autofill.CreditCardFillingInfoBar", metric,
NUM_INFO_BAR_METRICS);
}
// static
void AutofillMetrics::LogSaveCardPromptMetric(
SaveCardPromptMetric metric,
bool is_uploading,
bool is_reshow,
int previous_save_credit_card_prompt_user_decision) {
DCHECK_LT(metric, NUM_SAVE_CARD_PROMPT_METRICS);
std::string destination = is_uploading ? ".Upload" : ".Local";
std::string show = is_reshow ? ".Reshows" : ".FirstShow";
LogUMAHistogramEnumeration(
"Autofill.SaveCreditCardPrompt" + destination + show +
PreviousSaveCreditCardPromptUserDecisionToString(
previous_save_credit_card_prompt_user_decision),
metric, NUM_SAVE_CARD_PROMPT_METRICS);
}
// static
void AutofillMetrics::LogScanCreditCardPromptMetric(
ScanCreditCardPromptMetric metric) {
DCHECK_LT(metric, NUM_SCAN_CREDIT_CARD_PROMPT_METRICS);
UMA_HISTOGRAM_ENUMERATION("Autofill.ScanCreditCardPrompt", metric,
NUM_SCAN_CREDIT_CARD_PROMPT_METRICS);
}
// static
void AutofillMetrics::LogScanCreditCardCompleted(
const base::TimeDelta& duration,
bool completed) {
std::string suffix = completed ? "Completed" : "Cancelled";
LogUMAHistogramLongTimes("Autofill.ScanCreditCard.Duration_" + suffix,
duration);
UMA_HISTOGRAM_BOOLEAN("Autofill.ScanCreditCard.Completed", completed);
}
// static
void AutofillMetrics::LogUnmaskPromptEvent(UnmaskPromptEvent event) {
UMA_HISTOGRAM_ENUMERATION("Autofill.UnmaskPrompt.Events", event,
NUM_UNMASK_PROMPT_EVENTS);
}
// static
void AutofillMetrics::LogUnmaskPromptEventDuration(
const base::TimeDelta& duration,
UnmaskPromptEvent close_event) {
std::string suffix;
switch (close_event) {
case UNMASK_PROMPT_CLOSED_NO_ATTEMPTS:
suffix = "NoAttempts";
break;
case UNMASK_PROMPT_CLOSED_FAILED_TO_UNMASK_RETRIABLE_FAILURE:
case UNMASK_PROMPT_CLOSED_FAILED_TO_UNMASK_NON_RETRIABLE_FAILURE:
suffix = "Failure";
break;
case UNMASK_PROMPT_CLOSED_ABANDON_UNMASKING:
suffix = "AbandonUnmasking";
break;
case UNMASK_PROMPT_UNMASKED_CARD_FIRST_ATTEMPT:
case UNMASK_PROMPT_UNMASKED_CARD_AFTER_FAILED_ATTEMPTS:
suffix = "Success";
break;
default:
NOTREACHED();
return;
}
LogUMAHistogramLongTimes("Autofill.UnmaskPrompt.Duration", duration);
LogUMAHistogramLongTimes("Autofill.UnmaskPrompt.Duration." + suffix,
duration);
}
// static
void AutofillMetrics::LogTimeBeforeAbandonUnmasking(
const base::TimeDelta& duration) {
UMA_HISTOGRAM_LONG_TIMES("Autofill.UnmaskPrompt.TimeBeforeAbandonUnmasking",
duration);
}
// static
void AutofillMetrics::LogRealPanResult(
AutofillClient::PaymentsRpcResult result) {
PaymentsRpcResult metric_result;
switch (result) {
case AutofillClient::SUCCESS:
metric_result = PAYMENTS_RESULT_SUCCESS;
break;
case AutofillClient::TRY_AGAIN_FAILURE:
metric_result = PAYMENTS_RESULT_TRY_AGAIN_FAILURE;
break;
case AutofillClient::PERMANENT_FAILURE:
metric_result = PAYMENTS_RESULT_PERMANENT_FAILURE;
break;
case AutofillClient::NETWORK_ERROR:
metric_result = PAYMENTS_RESULT_NETWORK_ERROR;
break;
default:
NOTREACHED();
return;
}
UMA_HISTOGRAM_ENUMERATION("Autofill.UnmaskPrompt.GetRealPanResult",
metric_result, NUM_PAYMENTS_RESULTS);
}
// static
void AutofillMetrics::LogRealPanDuration(
const base::TimeDelta& duration,
AutofillClient::PaymentsRpcResult result) {
std::string suffix;
switch (result) {
case AutofillClient::SUCCESS:
suffix = "Success";
break;
case AutofillClient::TRY_AGAIN_FAILURE:
case AutofillClient::PERMANENT_FAILURE:
suffix = "Failure";
break;
case AutofillClient::NETWORK_ERROR:
suffix = "NetworkError";
break;
default:
NOTREACHED();
return;
}
LogUMAHistogramLongTimes("Autofill.UnmaskPrompt.GetRealPanDuration",
duration);
LogUMAHistogramLongTimes("Autofill.UnmaskPrompt.GetRealPanDuration." + suffix,
duration);
}
// static
void AutofillMetrics::LogUnmaskingDuration(
const base::TimeDelta& duration,
AutofillClient::PaymentsRpcResult result) {
std::string suffix;
switch (result) {
case AutofillClient::SUCCESS:
suffix = "Success";
break;
case AutofillClient::TRY_AGAIN_FAILURE:
case AutofillClient::PERMANENT_FAILURE:
suffix = "Failure";
break;
case AutofillClient::NETWORK_ERROR:
suffix = "NetworkError";
break;
default:
NOTREACHED();
return;
}
LogUMAHistogramLongTimes("Autofill.UnmaskPrompt.UnmaskingDuration", duration);
LogUMAHistogramLongTimes("Autofill.UnmaskPrompt.UnmaskingDuration." + suffix,
duration);
}
// static
void AutofillMetrics::LogDeveloperEngagementMetric(
DeveloperEngagementMetric metric) {
DCHECK_LT(metric, NUM_DEVELOPER_ENGAGEMENT_METRICS);
UMA_HISTOGRAM_ENUMERATION("Autofill.DeveloperEngagement", metric,
NUM_DEVELOPER_ENGAGEMENT_METRICS);
}
void AutofillMetrics::LogHeuristicPredictionQualityMetrics(
FormInteractionsUkmLogger* form_interactions_ukm_logger,
const FormStructure& form,
const AutofillField& field,
QualityMetricType metric_type) {
LogPredictionQualityMetrics(
PREDICTION_SOURCE_HEURISTIC,
AutofillType(field.heuristic_type()).GetStorableType(),
form_interactions_ukm_logger, form, field, metric_type);
}
void AutofillMetrics::LogServerPredictionQualityMetrics(
FormInteractionsUkmLogger* form_interactions_ukm_logger,
const FormStructure& form,
const AutofillField& field,
QualityMetricType metric_type) {
LogPredictionQualityMetrics(
PREDICTION_SOURCE_SERVER,
AutofillType(field.server_type()).GetStorableType(),
form_interactions_ukm_logger, form, field, metric_type);
}
void AutofillMetrics::LogOverallPredictionQualityMetrics(
FormInteractionsUkmLogger* form_interactions_ukm_logger,
const FormStructure& form,
const AutofillField& field,
QualityMetricType metric_type) {
LogPredictionQualityMetrics(
PREDICTION_SOURCE_OVERALL, field.Type().GetStorableType(),
form_interactions_ukm_logger, form, field, metric_type);
}
// static
void AutofillMetrics::LogServerQueryMetric(ServerQueryMetric metric) {
DCHECK_LT(metric, NUM_SERVER_QUERY_METRICS);
UMA_HISTOGRAM_ENUMERATION("Autofill.ServerQueryResponse", metric,
NUM_SERVER_QUERY_METRICS);
}
// static
void AutofillMetrics::LogUserHappinessMetric(UserHappinessMetric metric) {
DCHECK_LT(metric, NUM_USER_HAPPINESS_METRICS);
UMA_HISTOGRAM_ENUMERATION("Autofill.UserHappiness", metric,
NUM_USER_HAPPINESS_METRICS);
}
// static
void AutofillMetrics::LogFormFillDurationFromLoadWithAutofill(
const base::TimeDelta& duration) {
UMA_HISTOGRAM_CUSTOM_TIMES("Autofill.FillDuration.FromLoad.WithAutofill",
duration,
base::TimeDelta::FromMilliseconds(100),
base::TimeDelta::FromMinutes(10),
50);
}
// static
void AutofillMetrics::LogFormFillDurationFromLoadWithoutAutofill(
const base::TimeDelta& duration) {
UMA_HISTOGRAM_CUSTOM_TIMES("Autofill.FillDuration.FromLoad.WithoutAutofill",
duration,
base::TimeDelta::FromMilliseconds(100),
base::TimeDelta::FromMinutes(10),
50);
}
// static
void AutofillMetrics::LogFormFillDurationFromInteractionWithAutofill(
const base::TimeDelta& duration) {
UMA_HISTOGRAM_CUSTOM_TIMES(
"Autofill.FillDuration.FromInteraction.WithAutofill",
duration,
base::TimeDelta::FromMilliseconds(100),
base::TimeDelta::FromMinutes(10),
50);
}
// static
void AutofillMetrics::LogFormFillDurationFromInteractionWithoutAutofill(
const base::TimeDelta& duration) {
UMA_HISTOGRAM_CUSTOM_TIMES(
"Autofill.FillDuration.FromInteraction.WithoutAutofill",
duration,
base::TimeDelta::FromMilliseconds(100),
base::TimeDelta::FromMinutes(10),
50);
}
// static
void AutofillMetrics::LogIsAutofillEnabledAtStartup(bool enabled) {
UMA_HISTOGRAM_BOOLEAN("Autofill.IsEnabled.Startup", enabled);
}
// static
void AutofillMetrics::LogIsAutofillEnabledAtPageLoad(bool enabled) {
UMA_HISTOGRAM_BOOLEAN("Autofill.IsEnabled.PageLoad", enabled);
}
// static
void AutofillMetrics::LogStoredProfileCount(size_t num_profiles) {
UMA_HISTOGRAM_COUNTS("Autofill.StoredProfileCount", num_profiles);
}
// static
void AutofillMetrics::LogStoredLocalCreditCardCount(size_t num_local_cards) {
UMA_HISTOGRAM_COUNTS("Autofill.StoredLocalCreditCardCount", num_local_cards);
}
// static
void AutofillMetrics::LogStoredServerCreditCardCounts(
size_t num_masked_cards,
size_t num_unmasked_cards) {
UMA_HISTOGRAM_COUNTS_1000("Autofill.StoredServerCreditCardCount.Masked",
num_masked_cards);
UMA_HISTOGRAM_COUNTS_1000("Autofill.StoredServerCreditCardCount.Unmasked",
num_unmasked_cards);
}
// static
void AutofillMetrics::LogNumberOfProfilesAtAutofillableFormSubmission(
size_t num_profiles) {
UMA_HISTOGRAM_COUNTS(
"Autofill.StoredProfileCountAtAutofillableFormSubmission", num_profiles);
}
// static
void AutofillMetrics::LogHasModifiedProfileOnCreditCardFormSubmission(
bool has_modified_profile) {
UMA_HISTOGRAM_BOOLEAN("Autofill.HasModifiedProfile.CreditCardFormSubmission",
has_modified_profile);
}
// static
void AutofillMetrics::LogAddressSuggestionsCount(size_t num_suggestions) {
UMA_HISTOGRAM_COUNTS("Autofill.AddressSuggestionsCount", num_suggestions);
}
// static
void AutofillMetrics::LogAutofillSuggestionAcceptedIndex(int index) {
// A maximum of 50 is enforced to minimize the number of buckets generated.
UMA_HISTOGRAM_SPARSE_SLOWLY("Autofill.SuggestionAcceptedIndex",
std::min(index, 50));
base::RecordAction(base::UserMetricsAction("Autofill_SelectedSuggestion"));
}
// static
void AutofillMetrics::LogAutocompleteSuggestionAcceptedIndex(int index) {
// A maximum of 50 is enforced to minimize the number of buckets generated.
UMA_HISTOGRAM_SPARSE_SLOWLY("Autofill.SuggestionAcceptedIndex.Autocomplete",
std::min(index, 50));
}
// static
void AutofillMetrics::LogNumberOfEditedAutofilledFields(
size_t num_edited_autofilled_fields,
bool observed_submission) {
if (observed_submission) {
UMA_HISTOGRAM_COUNTS_1000(
"Autofill.NumberOfEditedAutofilledFieldsAtSubmission",
num_edited_autofilled_fields);
} else {
UMA_HISTOGRAM_COUNTS_1000(
"Autofill.NumberOfEditedAutofilledFieldsAtSubmission.NoSubmission",
num_edited_autofilled_fields);
}
}
// static
void AutofillMetrics::LogServerResponseHasDataForForm(bool has_data) {
UMA_HISTOGRAM_BOOLEAN("Autofill.ServerResponseHasDataForForm", has_data);
}
// static
void AutofillMetrics::LogProfileActionOnFormSubmitted(
AutofillProfileAction action) {
UMA_HISTOGRAM_ENUMERATION("Autofill.ProfileActionOnFormSubmitted", action,
AUTOFILL_PROFILE_ACTION_ENUM_SIZE);
}
// static
void AutofillMetrics::LogAutofillFormSubmittedState(
AutofillFormSubmittedState state,
AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) {
UMA_HISTOGRAM_ENUMERATION("Autofill.FormSubmittedState", state,
AUTOFILL_FORM_SUBMITTED_STATE_ENUM_SIZE);
switch (state) {
case NON_FILLABLE_FORM_OR_NEW_DATA:
base::RecordAction(
base::UserMetricsAction("Autofill_FormSubmitted_NonFillable"));
break;
case FILLABLE_FORM_AUTOFILLED_ALL:
base::RecordAction(
base::UserMetricsAction("Autofill_FormSubmitted_FilledAll"));
break;
case FILLABLE_FORM_AUTOFILLED_SOME:
base::RecordAction(
base::UserMetricsAction("Autofill_FormSubmitted_FilledSome"));
break;
case FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS:
base::RecordAction(base::UserMetricsAction(
"Autofill_FormSubmitted_FilledNone_SuggestionsShown"));
break;
case FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS:
base::RecordAction(base::UserMetricsAction(
"Autofill_FormSubmitted_FilledNone_SuggestionsNotShown"));
break;
default:
NOTREACHED();
break;
}
form_interactions_ukm_logger->LogFormSubmitted(state);
}
// static
void AutofillMetrics::LogDetermineHeuristicTypesTiming(
const base::TimeDelta& duration) {
UMA_HISTOGRAM_TIMES("Autofill.Timing.DetermineHeuristicTypes", duration);
}
// static
void AutofillMetrics::LogParseFormTiming(const base::TimeDelta& duration) {
UMA_HISTOGRAM_TIMES("Autofill.Timing.ParseForm", duration);
}
// static
void AutofillMetrics::LogNumberOfProfilesConsideredForDedupe(
size_t num_considered) {
// A maximum of 50 is enforced to reduce the number of generated buckets.
UMA_HISTOGRAM_COUNTS_1000("Autofill.NumberOfProfilesConsideredForDedupe",
std::min(int(num_considered), 50));
}
// static
void AutofillMetrics::LogNumberOfProfilesRemovedDuringDedupe(
size_t num_removed) {
// A maximum of 50 is enforced to reduce the number of generated buckets.
UMA_HISTOGRAM_COUNTS_1000("Autofill.NumberOfProfilesRemovedDuringDedupe",
std::min(int(num_removed), 50));
}
// static
void AutofillMetrics::LogIsQueriedCreditCardFormSecure(bool is_secure) {
UMA_HISTOGRAM_BOOLEAN("Autofill.QueriedCreditCardFormIsSecure", is_secure);
}
// static
void AutofillMetrics::LogWalletAddressConversionType(
WalletAddressConversionType type) {
DCHECK_LT(type, NUM_CONVERTED_ADDRESS_CONVERSION_TYPES);
UMA_HISTOGRAM_ENUMERATION("Autofill.WalletAddressConversionType", type,
NUM_CONVERTED_ADDRESS_CONVERSION_TYPES);
}
// static
void AutofillMetrics::LogShowedHttpNotSecureExplanation() {
base::RecordAction(
base::UserMetricsAction("Autofill_ShowedHttpNotSecureExplanation"));
}
// static
void AutofillMetrics::LogCardUploadDecisionsUkm(ukm::UkmRecorder* ukm_recorder,
const GURL& url,
int upload_decision_metrics) {
DCHECK(upload_decision_metrics);
DCHECK_LT(upload_decision_metrics, 1 << kNumCardUploadDecisionMetrics);
const std::vector<std::pair<const char*, int>> metrics = {
{internal::kUKMCardUploadDecisionMetricName, upload_decision_metrics}};
LogUkm(ukm_recorder, url, internal::kUKMCardUploadDecisionEntryName, metrics);
}
// static
void AutofillMetrics::LogDeveloperEngagementUkm(
ukm::UkmRecorder* ukm_recorder,
const GURL& url,
int developer_engagement_metrics) {
DCHECK(developer_engagement_metrics);
DCHECK_LT(developer_engagement_metrics,
1 << NUM_DEVELOPER_ENGAGEMENT_METRICS);
const std::vector<std::pair<const char*, int>> metrics = {
{internal::kUKMDeveloperEngagementMetricName,
developer_engagement_metrics}};
LogUkm(ukm_recorder, url, internal::kUKMDeveloperEngagementEntryName,
metrics);
}
// static
bool AutofillMetrics::LogUkm(
ukm::UkmRecorder* ukm_recorder,
const GURL& url,
const std::string& ukm_entry_name,
const std::vector<std::pair<const char*, int>>& metrics) {
if (!ukm_recorder || !url.is_valid() || metrics.empty()) {
return false;
}
ukm::SourceId source_id = ukm_recorder->GetNewSourceID();
ukm_recorder->UpdateSourceURL(source_id, url);
std::unique_ptr<ukm::UkmEntryBuilder> builder =
ukm_recorder->GetEntryBuilder(source_id, ukm_entry_name.c_str());
for (auto it = metrics.begin(); it != metrics.end(); ++it) {
builder->AddMetric(it->first, it->second);
}
return true;
}
AutofillMetrics::FormEventLogger::FormEventLogger(
bool is_for_credit_card,
AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger)
: is_for_credit_card_(is_for_credit_card),
server_record_type_count_(0),
local_record_type_count_(0),
is_context_secure_(false),
has_logged_interacted_(false),
has_logged_suggestions_shown_(false),
has_logged_masked_server_card_suggestion_selected_(false),
has_logged_suggestion_filled_(false),
has_logged_will_submit_(false),
has_logged_submitted_(false),
has_logged_bank_name_available_(false),
logged_suggestion_filled_was_server_data_(false),
logged_suggestion_filled_was_masked_server_card_(false),
form_interactions_ukm_logger_(form_interactions_ukm_logger) {}
void AutofillMetrics::FormEventLogger::OnDidInteractWithAutofillableForm() {
if (!has_logged_interacted_) {
has_logged_interacted_ = true;
form_interactions_ukm_logger_->LogInteractedWithForm(
is_for_credit_card_, local_record_type_count_,
server_record_type_count_);
Log(AutofillMetrics::FORM_EVENT_INTERACTED_ONCE);
}
}
void AutofillMetrics::FormEventLogger::OnDidPollSuggestions(
const FormFieldData& field) {
// Record only one poll user action for consecutive polls of the same field.
// This is to avoid recording too many poll actions (for example when a user
// types in a field, triggering multiple queries) to make the analysis more
// simple.
if (!field.SameFieldAs(last_polled_field_)) {
if (is_for_credit_card_) {
base::RecordAction(
base::UserMetricsAction("Autofill_PolledCreditCardSuggestions"));
} else {
base::RecordAction(
base::UserMetricsAction("Autofill_PolledProfileSuggestions"));
}
last_polled_field_ = field;
}
}
void AutofillMetrics::FormEventLogger::OnDidShowSuggestions(
const AutofillField& field) {
form_interactions_ukm_logger_->LogSuggestionsShown(field);
Log(AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN);
if (!has_logged_suggestions_shown_) {
has_logged_suggestions_shown_ = true;
Log(AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE);
if (has_logged_bank_name_available_) {
Log(AutofillMetrics::
FORM_EVENT_SUGGESTIONS_SHOWN_WITH_BANK_NAME_AVAILABLE_ONCE);
}
}
if (is_for_credit_card_) {
base::RecordAction(
base::UserMetricsAction("Autofill_ShowedCreditCardSuggestions"));
} else {
base::RecordAction(
base::UserMetricsAction("Autofill_ShowedProfileSuggestions"));
}
}
void AutofillMetrics::FormEventLogger::OnDidSelectMaskedServerCardSuggestion() {
DCHECK(is_for_credit_card_);
form_interactions_ukm_logger_->LogSelectedMaskedServerCard();
Log(AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED);
if (!has_logged_masked_server_card_suggestion_selected_) {
has_logged_masked_server_card_suggestion_selected_ = true;
Log(AutofillMetrics::
FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE);
}
}
void AutofillMetrics::FormEventLogger::OnDidFillSuggestion(
const CreditCard& credit_card) {
DCHECK(is_for_credit_card_);
form_interactions_ukm_logger_->LogDidFillSuggestion(
static_cast<int>(credit_card.record_type()));
if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD)
Log(AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED);
else if (credit_card.record_type() == CreditCard::FULL_SERVER_CARD)
Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED);
else
Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED);
if (!has_logged_suggestion_filled_) {
has_logged_suggestion_filled_ = true;
logged_suggestion_filled_was_server_data_ =
credit_card.record_type() == CreditCard::MASKED_SERVER_CARD ||
credit_card.record_type() == CreditCard::FULL_SERVER_CARD;
logged_suggestion_filled_was_masked_server_card_ =
credit_card.record_type() == CreditCard::MASKED_SERVER_CARD;
if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD) {
Log(AutofillMetrics::
FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE);
} else if (credit_card.record_type() == CreditCard::FULL_SERVER_CARD) {
Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE);
if (has_logged_bank_name_available_) {
Log(AutofillMetrics::
FORM_EVENT_SERVER_SUGGESTION_FILLED_WITH_BANK_NAME_AVAILABLE_ONCE);
}
} else {
Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE);
}
}
base::RecordAction(
base::UserMetricsAction("Autofill_FilledCreditCardSuggestion"));
}
void AutofillMetrics::FormEventLogger::OnDidFillSuggestion(
const AutofillProfile& profile) {
DCHECK(!is_for_credit_card_);
form_interactions_ukm_logger_->LogDidFillSuggestion(
static_cast<int>(profile.record_type()));
if (profile.record_type() == AutofillProfile::SERVER_PROFILE)
Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED);
else
Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED);
if (!has_logged_suggestion_filled_) {
has_logged_suggestion_filled_ = true;
logged_suggestion_filled_was_server_data_ =
profile.record_type() == AutofillProfile::SERVER_PROFILE;
Log(profile.record_type() == AutofillProfile::SERVER_PROFILE
? AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE
: AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE);
}
base::RecordAction(
base::UserMetricsAction("Autofill_FilledProfileSuggestion"));
}
void AutofillMetrics::FormEventLogger::OnWillSubmitForm() {
// Not logging this kind of form if we haven't logged a user interaction.
if (!has_logged_interacted_)
return;
// Not logging twice.
if (has_logged_will_submit_)
return;
has_logged_will_submit_ = true;
if (!has_logged_suggestion_filled_) {
Log(AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE);
} else if (logged_suggestion_filled_was_masked_server_card_) {
Log(AutofillMetrics::
FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE);
} else if (logged_suggestion_filled_was_server_data_) {
Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE);
} else {
Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE);
}
if (has_logged_suggestions_shown_) {
Log(AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE);
}
base::RecordAction(base::UserMetricsAction("Autofill_OnWillSubmitForm"));
}
void AutofillMetrics::FormEventLogger::OnFormSubmitted() {
// Not logging this kind of form if we haven't logged a user interaction.
if (!has_logged_interacted_)
return;
// Not logging twice.
if (has_logged_submitted_)
return;
has_logged_submitted_ = true;
if (!has_logged_suggestion_filled_) {
Log(AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE);
} else if (logged_suggestion_filled_was_masked_server_card_) {
Log(AutofillMetrics::
FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE);
} else if (logged_suggestion_filled_was_server_data_) {
Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE);
} else {
Log(AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE);
}
if (has_logged_suggestions_shown_) {
Log(AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE);
}
}
void AutofillMetrics::FormEventLogger::SetBankNameAvailable() {
has_logged_bank_name_available_ = true;
}
void AutofillMetrics::FormEventLogger::Log(FormEvent event) const {
DCHECK_LT(event, NUM_FORM_EVENTS);
std::string name("Autofill.FormEvents.");
if (is_for_credit_card_)
name += "CreditCard";
else
name += "Address";
LogUMAHistogramEnumeration(name, event, NUM_FORM_EVENTS);
// Log again in a different histogram for credit card forms on nonsecure
// pages, so that form interactions on nonsecure pages can be analyzed on
// their own.
if (is_for_credit_card_ && !is_context_secure_) {
LogUMAHistogramEnumeration(name + ".OnNonsecurePage", event,
NUM_FORM_EVENTS);
}
// Logging again in a different histogram for segmentation purposes.
// TODO(waltercacau): Re-evaluate if we still need such fine grained
// segmentation. http://crbug.com/454018
if (server_record_type_count_ == 0 && local_record_type_count_ == 0)
name += ".WithNoData";
else if (server_record_type_count_ > 0 && local_record_type_count_ == 0)
name += ".WithOnlyServerData";
else if (server_record_type_count_ == 0 && local_record_type_count_ > 0)
name += ".WithOnlyLocalData";
else
name += ".WithBothServerAndLocalData";
LogUMAHistogramEnumeration(name, event, NUM_FORM_EVENTS);
}
void AutofillMetrics::FormEventLogger::Log(
BankNameDisplayedFormEvent event) const {
DCHECK_LT(event, BANK_NAME_NUM_FORM_EVENTS);
std::string name("Autofill.FormEvents.CreditCard.BankNameDisplayed");
LogUMAHistogramEnumeration(name, event, BANK_NAME_NUM_FORM_EVENTS);
}
AutofillMetrics::FormInteractionsUkmLogger::FormInteractionsUkmLogger(
ukm::UkmRecorder* ukm_recorder)
: ukm_recorder_(ukm_recorder) {}
void AutofillMetrics::FormInteractionsUkmLogger::OnFormsParsed(
const GURL& url) {
if (ukm_recorder_ == nullptr)
return;
url_ = url;
form_parsed_timestamp_ = base::TimeTicks::Now();
}
void AutofillMetrics::FormInteractionsUkmLogger::LogInteractedWithForm(
bool is_for_credit_card,
size_t local_record_type_count,
size_t server_record_type_count) {
if (!CanLog())
return;
if (source_id_ == -1)
GetNewSourceID();
std::unique_ptr<ukm::UkmEntryBuilder> builder =
ukm_recorder_->GetEntryBuilder(source_id_,
internal::kUKMInteractedWithFormEntryName);
builder->AddMetric(internal::kUKMIsForCreditCardMetricName,
is_for_credit_card);
builder->AddMetric(internal::kUKMLocalRecordTypeCountMetricName,
local_record_type_count);
builder->AddMetric(internal::kUKMServerRecordTypeCountMetricName,
server_record_type_count);
}
void AutofillMetrics::FormInteractionsUkmLogger::LogSuggestionsShown(
const AutofillField& field) {
if (!CanLog())
return;
if (source_id_ == -1)
GetNewSourceID();
std::unique_ptr<ukm::UkmEntryBuilder> builder =
ukm_recorder_->GetEntryBuilder(source_id_,
internal::kUKMSuggestionsShownEntryName);
builder->AddMetric(internal::kUKMHeuristicTypeMetricName,
static_cast<int>(field.heuristic_type()));
builder->AddMetric(internal::kUKMHtmlFieldTypeMetricName,
static_cast<int>(field.html_type()));
builder->AddMetric(internal::kUKMServerTypeMetricName,
static_cast<int>(field.server_type()));
builder->AddMetric(internal::kUKMMillisecondsSinceFormParsedMetricName,
MillisecondsSinceFormParsed());
}
void AutofillMetrics::FormInteractionsUkmLogger::LogSelectedMaskedServerCard() {
if (!CanLog())
return;
if (source_id_ == -1)
GetNewSourceID();
std::unique_ptr<ukm::UkmEntryBuilder> builder =
ukm_recorder_->GetEntryBuilder(
source_id_, internal::kUKMSelectedMaskedServerCardEntryName);
builder->AddMetric(internal::kUKMMillisecondsSinceFormParsedMetricName,
MillisecondsSinceFormParsed());
}
void AutofillMetrics::FormInteractionsUkmLogger::LogDidFillSuggestion(
int record_type) {
if (!CanLog())
return;
if (source_id_ == -1)
GetNewSourceID();
std::unique_ptr<ukm::UkmEntryBuilder> builder =
ukm_recorder_->GetEntryBuilder(source_id_,
internal::kUKMSuggestionFilledEntryName);
builder->AddMetric(internal::kUKMRecordTypeMetricName, record_type);
builder->AddMetric(internal::kUKMMillisecondsSinceFormParsedMetricName,
MillisecondsSinceFormParsed());
}
void AutofillMetrics::FormInteractionsUkmLogger::LogTextFieldDidChange(
const AutofillField& field) {
if (!CanLog())
return;
if (source_id_ == -1)
GetNewSourceID();
std::unique_ptr<ukm::UkmEntryBuilder> builder =
ukm_recorder_->GetEntryBuilder(source_id_,
internal::kUKMTextFieldDidChangeEntryName);
builder->AddMetric(internal::kUKMFieldTypeGroupMetricName,
static_cast<int>(field.Type().group()));
builder->AddMetric(internal::kUKMHeuristicTypeMetricName,
static_cast<int>(field.heuristic_type()));
builder->AddMetric(internal::kUKMServerTypeMetricName,
static_cast<int>(field.server_type()));
builder->AddMetric(internal::kUKMHtmlFieldTypeMetricName,
static_cast<int>(field.html_type()));
builder->AddMetric(internal::kUKMHtmlFieldModeMetricName,
static_cast<int>(field.html_mode()));
builder->AddMetric(internal::kUKMIsAutofilledMetricName, field.is_autofilled);
builder->AddMetric(internal::kUKMIsEmptyMetricName, field.IsEmpty());
builder->AddMetric(internal::kUKMMillisecondsSinceFormParsedMetricName,
MillisecondsSinceFormParsed());
}
void AutofillMetrics::FormInteractionsUkmLogger::LogFieldFillStatus(
const FormStructure& form,
const AutofillField& field,
QualityMetricType metric_type) {
if (!CanLog())
return;
if (source_id_ == -1)
GetNewSourceID();
std::unique_ptr<ukm::UkmEntryBuilder> builder =
ukm_recorder_->GetEntryBuilder(source_id_,
internal::kUKMFieldFillStatusEntryName);
builder->AddMetric(internal::kUKMMillisecondsSinceFormParsedMetricName,
MillisecondsSinceFormParsed());
builder->AddMetric(internal::kUKMFormSignatureMetricName,
static_cast<int64_t>(form.form_signature()));
builder->AddMetric(internal::kUKMFieldSignatureMetricName,
static_cast<int64_t>(field.GetFieldSignature()));
builder->AddMetric(internal::kUKMValidationEventMetricName,
static_cast<int64_t>(metric_type));
builder->AddMetric(internal::kUKMIsAutofilledMetricName,
static_cast<int64_t>(field.is_autofilled));
builder->AddMetric(internal::kUKMWasPreviouslyAutofilledMetricName,
static_cast<int64_t>(field.previously_autofilled()));
}
void AutofillMetrics::FormInteractionsUkmLogger::LogFieldType(
FormSignature form_signature,
FieldSignature field_signature,
QualityMetricPredictionSource prediction_source,
QualityMetricType metric_type,
ServerFieldType predicted_type,
ServerFieldType actual_type) {
if (!CanLog())
return;
if (source_id_ == -1)
GetNewSourceID();
std::unique_ptr<ukm::UkmEntryBuilder> builder =
ukm_recorder_->GetEntryBuilder(source_id_,
internal::kUKMFieldTypeEntryName);
builder->AddMetric(internal::kUKMMillisecondsSinceFormParsedMetricName,
MillisecondsSinceFormParsed());
builder->AddMetric(internal::kUKMFormSignatureMetricName,
static_cast<int64_t>(form_signature));
builder->AddMetric(internal::kUKMFieldSignatureMetricName,
static_cast<int64_t>(field_signature));
builder->AddMetric(internal::kUKMValidationEventMetricName,
static_cast<int64_t>(metric_type));
builder->AddMetric(internal::kUKMPredictionSourceMetricName,
static_cast<int64_t>(prediction_source));
builder->AddMetric(internal::kUKMPredictedTypeMetricName,
static_cast<int64_t>(predicted_type));
builder->AddMetric(internal::kUKMActualTypeMetricName,
static_cast<int64_t>(actual_type));
}
void AutofillMetrics::FormInteractionsUkmLogger::LogFormSubmitted(
AutofillFormSubmittedState state) {
if (!CanLog())
return;
if (source_id_ == -1)
GetNewSourceID();
std::unique_ptr<ukm::UkmEntryBuilder> builder =
ukm_recorder_->GetEntryBuilder(source_id_,
internal::kUKMFormSubmittedEntryName);
builder->AddMetric(internal::kUKMAutofillFormSubmittedStateMetricName,
static_cast<int>(state));
if (form_parsed_timestamp_.is_null())
DCHECK(state == NON_FILLABLE_FORM_OR_NEW_DATA ||
state == FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS)
<< state;
else
builder->AddMetric(internal::kUKMMillisecondsSinceFormParsedMetricName,
MillisecondsSinceFormParsed());
}
void AutofillMetrics::FormInteractionsUkmLogger::UpdateSourceURL(
const GURL& url) {
url_ = url;
if (CanLog())
ukm_recorder_->UpdateSourceURL(source_id_, url_);
}
bool AutofillMetrics::FormInteractionsUkmLogger::CanLog() const {
return ukm_recorder_ && url_.is_valid();
}
int64_t
AutofillMetrics::FormInteractionsUkmLogger::MillisecondsSinceFormParsed()
const {
DCHECK(!form_parsed_timestamp_.is_null());
// Use the pinned timestamp as the current time if it's set.
base::TimeTicks now =
pinned_timestamp_.is_null() ? base::TimeTicks::Now() : pinned_timestamp_;
return (now - form_parsed_timestamp_).InMilliseconds();
}
void AutofillMetrics::FormInteractionsUkmLogger::GetNewSourceID() {
source_id_ = ukm_recorder_->GetNewSourceID();
ukm_recorder_->UpdateSourceURL(source_id_, url_);
}
AutofillMetrics::UkmTimestampPin::UkmTimestampPin(
FormInteractionsUkmLogger* logger)
: logger_(logger) {
DCHECK(logger_);
DCHECK(!logger_->has_pinned_timestamp());
logger_->set_pinned_timestamp(base::TimeTicks::Now());
}
AutofillMetrics::UkmTimestampPin::~UkmTimestampPin() {
DCHECK(logger_->has_pinned_timestamp());
logger_->set_pinned_timestamp(base::TimeTicks());
}
} // namespace autofill