Skip to content

Commit

Permalink
[TTFCC] Add FillingCorrectness metric for payments bottom sheet
Browse files Browse the repository at this point in the history
This CL introduces a metric to track if the submitted form required some user editing after autofilling it with the suggestion selected in TTF.

Note: This metric is a separate additional metric to the general FillingCorrectness metric which tracks any autofilled form, no matter by which method.

Bug: 1247698
Change-Id: I4ed11ca5c1f637fc1593e9499157b9133a4cc9a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4161710
Reviewed-by: Matthias Körber <koerber@google.com>
Reviewed-by: Viktor Semeniuk <vsemeniuk@google.com>
Commit-Queue: Anna Tsvirchkova <atsvirchkova@google.com>
Cr-Commit-Position: refs/heads/main@{#1096803}
  • Loading branch information
Anna Tsvirchkova authored and Chromium LUCI CQ committed Jan 25, 2023
1 parent 41a6083 commit d6a1e5d
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 55 deletions.
7 changes: 4 additions & 3 deletions components/autofill/core/browser/browser_autofill_manager.cc
Expand Up @@ -3340,9 +3340,10 @@ void BrowserAutofillManager::OnSeePromoCodeOfferDetailsSelected(
OnSingleFieldSuggestionSelected(value, frontend_id);
}

void BrowserAutofillManager::SetSuggestionOriginMetricState(
AutofillSuggestionMethod state) {
autofill_suggestion_method_ = state;
void BrowserAutofillManager::SetAutofillSuggestionMethod(
AutofillSuggestionMethod method) {
autofill_suggestion_method_ = method;
credit_card_form_event_logger_->set_autofill_suggestion_method(method);
}

void BrowserAutofillManager::SetShouldSuppressKeyboard(bool suppress) {
Expand Down
Expand Up @@ -313,7 +313,7 @@ class BrowserAutofillManager : public AutofillManager,

// Sets where the accepted autofill suggestion came from: touch to fill,
// keyboard accessory, etc.
virtual void SetSuggestionOriginMetricState(AutofillSuggestionMethod state);
virtual void SetAutofillSuggestionMethod(AutofillSuggestionMethod state);

// Forwards call to the same-named `AutofillDriver` function.
virtual void SetShouldSuppressKeyboard(bool suppress);
Expand Down
Expand Up @@ -7,6 +7,7 @@
#include "components/autofill/core/browser/autofill_form_test_utils.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/payments/credit_card_access_manager.h"
#include "components/autofill/core/browser/touch_to_fill_delegate_impl.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/autofill_features.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
Expand Down Expand Up @@ -77,6 +78,12 @@ void AutofillMetricsBaseTest::SetUp() {
external_delegate_ = external_delegate.get();
autofill_manager().SetExternalDelegateForTest(std::move(external_delegate));

auto touch_to_fill_delegate_uptr =
std::make_unique<TouchToFillDelegateImpl>(&autofill_manager());
touch_to_fill_delgate_ = touch_to_fill_delegate_uptr.get();
autofill_manager().SetTouchToFillDelegateImplForTest(
std::move(touch_to_fill_delegate_uptr));

#if !BUILDFLAG(IS_IOS)
autofill_manager()
.GetCreditCardAccessManager()
Expand Down
Expand Up @@ -186,6 +186,7 @@ class AutofillMetricsBaseTest : public testing::Test {
syncer::TestSyncService sync_service_;
std::unique_ptr<TestAutofillDriver> autofill_driver_;
raw_ptr<AutofillExternalDelegate> external_delegate_;
raw_ptr<TouchToFillDelegateImpl> touch_to_fill_delgate_;

private:
void CreateTestAutofillProfiles();
Expand Down
134 changes: 87 additions & 47 deletions components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

#include "components/autofill/core/browser/metrics/autofill_metrics.h"
#include "base/check.h"

#include <stddef.h>

Expand Down Expand Up @@ -746,80 +747,119 @@ TEST_P(AutofillPerfectFillingMetricsTest,
BucketsAre(test_case.credit_card_buckets));
}

struct SuggestionOriginPerfectFillingTestCase {
std::string description;
struct TouchToFillForCreditCardsTestCase {
std::vector<Field> fields;
bool expected_metric_value;
std::vector<bool> fields_is_autofilled_values;
bool is_all_autofilled;
bool is_all_accepted;
};

class SuggestionOriginPerfectFillingMetricsTest
class TouchToFillForCreditCardsTest
: public AutofillMetricsTest,
public ::testing::WithParamInterface<
SuggestionOriginPerfectFillingTestCase> {
public ::testing::WithParamInterface<TouchToFillForCreditCardsTestCase> {
public:
std::vector<test::FieldDescription> GetFields(std::vector<Field> fields) {
std::vector<test::FieldDescription> fields_to_return;
std::vector<FormFieldData> GetFields(std::vector<Field> fields) {
std::vector<FormFieldData> fields_to_return;
fields_to_return.reserve(fields.size());
for (const auto& field : fields) {
test::FieldDescription f;
if (field.value) {
f.value = field.value;
} else if (field.field_type == CREDIT_CARD_NAME_FULL) {
f.value = u"Elvis Aaron Presley";
if (field.field_type == CREDIT_CARD_NAME_FULL) {
fields_to_return.push_back(
CreateField("Name on card", "cardName", "", "text"));
} else if (field.field_type == CREDIT_CARD_NUMBER) {
f.value = u"01230123012399";
fields_to_return.push_back(
CreateField("Credit card number", "cardNumber", "", "text"));
} else if (field.field_type == CREDIT_CARD_EXP_MONTH) {
fields_to_return.push_back(
CreateField("Expiration date", "cc_exp", "", "text"));
} else if (field.field_type == CREDIT_CARD_VERIFICATION_CODE) {
fields_to_return.push_back(CreateField("CVC", "CVC", "", "text"));
} else {
NOTREACHED();
}
f.role = field.field_type;
f.is_autofilled = field.is_autofilled;
fields_to_return.push_back(f);
}
return fields_to_return;
}
};

TEST_P(SuggestionOriginPerfectFillingMetricsTest,
PerfectFilling_TouchToFill_CreditCards) {
SuggestionOriginPerfectFillingTestCase test_case = GetParam();
std::vector<Field> fields{{CREDIT_CARD_NAME_FULL}, {CREDIT_CARD_NUMBER}};
FormData form =
test::GetFormData({.description_for_logging = test_case.description,
.fields = GetFields(test_case.fields),
.unique_renderer_id = test::MakeFormRendererId(),
.main_frame_origin = url::Origin::Create(
autofill_client_->form_origin())});

std::vector<ServerFieldType> field_types;
for (const auto& f : test_case.fields) {
field_types.push_back(f.field_type);
void SetFieldsAutofilledValues(FormData& form,
std::vector<bool>& fields_is_autofilled_values,
std::vector<Field>& server_field_types) {
DCHECK(form.fields.size() == fields_is_autofilled_values.size());
DCHECK(form.fields.size() == server_field_types.size());
for (size_t i = 0; i < fields_is_autofilled_values.size(); i++) {
form.fields[i].is_autofilled = fields_is_autofilled_values[i];
CreditCard testCard = test::GetCreditCard();
form.fields[i].value =
server_field_types[i].field_type != CREDIT_CARD_VERIFICATION_CODE
? testCard.GetRawInfo(server_field_types[i].field_type)
: u"123";
}
}
};

autofill_manager().AddSeenForm(form, field_types);
TEST_P(TouchToFillForCreditCardsTest,
AllAutofilledAndAccepted_TouchToFill_CreditCards) {
RecreateCreditCards(true, false, false, false);
TouchToFillForCreditCardsTestCase test_case = GetParam();
FormData form = CreateForm(GetFields(test_case.fields));

SeeForm(form);
autofill_manager().OnAskForValuesToFillTest(form, form.fields[0], {},
AutoselectFirstSuggestion(false),
FormElementWasClicked(true));

base::HistogramTester histogram_tester;
autofill_manager().SetSuggestionOriginMetricState(
AutofillSuggestionMethod::KTouchToFillCreditCard);
// Simulate user selection in the payments bottom sheet
touch_to_fill_delgate_->SuggestionSelected(kTestLocalCardId);
// Simulate that fields were autofilled
SetFieldsAutofilledValues(form, test_case.fields_is_autofilled_values,
test_case.fields);
// Simulate user made change to autofilled field
if (!test_case.is_all_accepted) {
SimulateUserChangedTextField(form, form.fields[0]);
}

SubmitForm(form);
ResetDriverToCommitMetrics();
EXPECT_EQ(histogram_tester.GetBucketCount(
"Autofill.TouchToFill.CreditCard.PerfectFilling",
test_case.expected_metric_value),
test_case.is_all_autofilled && test_case.is_all_accepted),
1);
EXPECT_EQ(histogram_tester.GetBucketCount(
"Autofill.FillingCorrectnessByMethod.CreditCard.TouchToFill",
test_case.is_all_accepted),
1);
}

INSTANTIATE_TEST_SUITE_P(
AutofillMetricsTest,
SuggestionOriginPerfectFillingMetricsTest,
TouchToFillForCreditCardsTest,
testing::Values(
// Test that we log the perfect filling metric correctly for an address
// form in which every field is autofilled.
SuggestionOriginPerfectFillingTestCase{
"PerfectFillingForCreditCardForm_AutofilledFromTTF",
{{CREDIT_CARD_NAME_FULL}, {CREDIT_CARD_NUMBER}},
true},
SuggestionOriginPerfectFillingTestCase{
"PerfectFillingForCreditCardForm_NotAllAutofilledFromTTF",
{{CREDIT_CARD_NAME_FULL}, {CREDIT_CARD_NUMBER, false}},
false}));
// All autofilled and nothing edited manually
TouchToFillForCreditCardsTestCase{
{{CREDIT_CARD_NAME_FULL},
{CREDIT_CARD_NUMBER},
{CREDIT_CARD_EXP_MONTH}},
/*fields_is_autofilled_values=*/{true, true, true},
/*is_all_autofilled=*/true,
/*is_all_accepted=*/true},
// Not all autofilled and nothing edited manually
TouchToFillForCreditCardsTestCase{
{{CREDIT_CARD_NAME_FULL},
{CREDIT_CARD_NUMBER},
{CREDIT_CARD_EXP_MONTH},
{CREDIT_CARD_VERIFICATION_CODE}},
/*fields_is_autofilled_values=*/{true, true, true, false},
/*is_all_autofilled=*/false,
/*is_all_accepted=*/true},
// Not all autofilled and something edited manually
TouchToFillForCreditCardsTestCase{
{{CREDIT_CARD_NAME_FULL},
{CREDIT_CARD_NUMBER},
{CREDIT_CARD_EXP_MONTH},
{CREDIT_CARD_VERIFICATION_CODE}},
/*fields_is_autofilled_values=*/{true, true, true, false},
/*is_all_autofilled=*/false,
/*is_all_accepted=*/false}));

// Test the emission of collisions between NUMERIC_QUANTITY and server
// predictions as well as the potential false positives.
Expand Down
12 changes: 12 additions & 0 deletions components/autofill/core/browser/metrics/autofill_metrics_utils.cc
Expand Up @@ -81,4 +81,16 @@ FieldFillingStatus GetFieldFillingStatus(const AutofillField& field) {
return FieldFillingStatus::kManuallyFilledToDifferentType;
}

std::string GetMetricsSuffixByAutofillMethod(AutofillSuggestionMethod method) {
switch (method) {
case AutofillSuggestionMethod::KTouchToFillCreditCard:
return "TouchToFill";
case AutofillSuggestionMethod::kUnknown:
NOTREACHED();
break;
}
NOTREACHED();
return "";
}

} // namespace autofill
Expand Up @@ -6,6 +6,7 @@
#define COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_AUTOFILL_METRICS_UTILS_H_

#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/metrics/autofill_metrics.h"

namespace autofill {
Expand Down Expand Up @@ -51,6 +52,11 @@ struct FormGroupFillingStats {
AutofillMetrics::FieldFillingStatus GetFieldFillingStatus(
const AutofillField& field);

// Returns the suffix for metrics histogram name depending on the autofill
// suggestions method used to fill in the value and the form type.
// E. g. "CreditCard.TouchToFill".
std::string GetMetricsSuffixByAutofillMethod(AutofillSuggestionMethod method);

} // namespace autofill

#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_METRICS_AUTOFILL_METRICS_UTILS_H_
Expand Up @@ -11,7 +11,9 @@
#include "base/strings/strcat.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/form_parsing/form_field.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/logging/log_manager.h"
#include "components/autofill/core/browser/metrics/autofill_metrics_utils.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_internals/log_message.h"
#include "components/autofill/core/common/autofill_internals/logging_scope.h"
Expand Down Expand Up @@ -275,7 +277,6 @@ void FormEventLoggerBase::RecordFunnelAndKeyMetrics() {

LOG_AF(funnel_rows) << Tr{} << "Form Type: " << form_type_name_;
LOG_AF(key_metrics_rows) << Tr{} << "Form Type: " << form_type_name_;

UmaHistogramBoolean("Autofill.Funnel.ParsedAsType." + form_type_name_,
has_parsed_form_);
// Log chronological funnel.
Expand Down Expand Up @@ -343,6 +344,8 @@ void FormEventLoggerBase::RecordFunnelAndKeyMetrics() {
!has_logged_edited_autofilled_field_);
LOG_AF(key_metrics_rows) << Tr{} << "FillingCorrectness"
<< !has_logged_edited_autofilled_field_;
LogFillingCorrectnessByFillingMethod(autofill_suggestion_method_,
form_type_name_);
}
// Whether a submitted form was filled.
UmaHistogramBoolean(
Expand Down Expand Up @@ -487,4 +490,19 @@ void FormEventLoggerBase::UpdateFlowId() {
flow_id_ = client_->GetCurrentFormInteractionsFlowId();
}

void FormEventLoggerBase::LogFillingCorrectnessByFillingMethod(
AutofillSuggestionMethod method,
const std::string& form_type) {
if (method == AutofillSuggestionMethod::kUnknown) {
return;
}

// Log the correctness metric by autofill suggestion method (touch to fill,
// keyboard accessory, etc) and the form type (credit card, address, etc.)
const std::string method_name = GetMetricsSuffixByAutofillMethod(method);
base::UmaHistogramBoolean(
"Autofill.FillingCorrectnessByMethod." + form_type + "." + method_name,
!has_logged_edited_autofilled_field_);
}

} // namespace autofill
Expand Up @@ -38,6 +38,10 @@ class FormEventLoggerBase {
local_record_type_count_ = local_record_type_count;
}

inline void set_autofill_suggestion_method(AutofillSuggestionMethod method) {
autofill_suggestion_method_ = method;
}

void OnDidInteractWithAutofillableForm(const FormStructure& form,
AutofillSyncSigninState sync_state);

Expand Down Expand Up @@ -135,6 +139,12 @@ class FormEventLoggerBase {

void UpdateFlowId();

// Records UMA metric: correctness filling in relation to the autofill
// suggestion method that was used (touch to fill for credit cards, keyboard
// accessory, etc.) and the form type (credit card, address, etc.)
void LogFillingCorrectnessByFillingMethod(AutofillSuggestionMethod method,
const std::string& form_type);

// Constructor parameters.
std::string form_type_name_;
bool is_in_any_main_frame_;
Expand All @@ -158,6 +168,8 @@ class FormEventLoggerBase {
false;
AblationGroup ablation_group_ = AblationGroup::kDefault;
AblationGroup conditional_ablation_group_ = AblationGroup::kDefault;
AutofillSuggestionMethod autofill_suggestion_method_ =
AutofillSuggestionMethod::kUnknown;
absl::optional<base::TimeDelta> time_from_interaction_to_submission_;

// The last field that was polled for suggestions.
Expand Down
Expand Up @@ -143,7 +143,7 @@ void TouchToFillDelegateImpl::SuggestionSelected(std::string unique_id) {
CreditCard* card = pdm->GetCreditCardByGUID(unique_id);
manager_->FillOrPreviewCreditCardForm(mojom::RendererFormDataAction::kFill,
query_form_, query_field_, card);
manager_->SetSuggestionOriginMetricState(
manager_->SetAutofillSuggestionMethod(
AutofillSuggestionMethod::KTouchToFillCreditCard);
}

Expand Down
Expand Up @@ -108,7 +108,7 @@ class MockBrowserAutofillManager : public TestBrowserAutofillManager {
const FormFieldData& field,
const CreditCard* credit_card));
MOCK_METHOD(void,
SetSuggestionOriginMetricState,
SetAutofillSuggestionMethod,
(AutofillSuggestionMethod state),
(override));
};
Expand Down Expand Up @@ -534,7 +534,7 @@ TEST_F(TouchToFillDelegateImplUnitTest, CardSelectionFillsCardForm) {
TryToShowTouchToFill(/*expected_success=*/true);

EXPECT_CALL(*browser_autofill_manager_, FillOrPreviewCreditCardForm);
EXPECT_CALL(*browser_autofill_manager_, SetSuggestionOriginMetricState);
EXPECT_CALL(*browser_autofill_manager_, SetAutofillSuggestionMethod);
touch_to_fill_delegate_->SuggestionSelected(credit_card.server_id());
}

Expand Down
20 changes: 20 additions & 0 deletions tools/metrics/histograms/metadata/autofill/histograms.xml
Expand Up @@ -1654,6 +1654,26 @@ chromium-metrics-reviews@google.com.
</summary>
</histogram>

<histogram name="Autofill.FillingCorrectnessByMethod.{FormType}.{Method}"
enum="BooleanAutofillFillingCorrectness" expires_after="2023-06-01">
<owner>atsvirhckova@google.com</owner>
<owner>izuzic@google.com</owner>
<owner>chrome-autofill-team@google.com</owner>
<summary>
Tracks whether or not the user has edited at least one of the autofilled
fields before submitting the form. This metric is tracked whenever the value
to autofill the {FormType} is provided by the {Method}. The metric is
recorded on form submission.
</summary>
<token key="FormType">
<variant name="Address" summary="Address form"/>
<variant name="CreditCard" summary="Credit card form"/>
</token>
<token key="Method">
<variant name="TouchToFill" summary="Bottom sheet"/>
</token>
</histogram>

<histogram name="Autofill.FindFormControlElementByUniqueRendererIdDuration"
units="microseconds" expires_after="2022-10-30">
<obsolete>
Expand Down

0 comments on commit d6a1e5d

Please sign in to comment.