blob: ee8a503e718b4d53afa93fdd9db0ffb83c6f0beb [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/form_structure.h"
#include <stddef.h>
#include <memory>
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/signatures_util.h"
#include "components/variations/entropy_provider.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using base::ASCIIToUTF16;
using autofill::features::kAutofillEnforceMinRequiredFieldsForHeuristics;
using autofill::features::kAutofillEnforceMinRequiredFieldsForQuery;
using autofill::features::kAutofillEnforceMinRequiredFieldsForUpload;
using autofill::features::kAutofillRationalizeRepeatedServerPredictions;
namespace autofill {
class FormStructureTest : public testing::Test {
public:
static std::string Hash64Bit(const std::string& str) {
return base::NumberToString(StrToHash64Bit(str));
}
void SetUp() override {
// By default this trial is enabled on tests.
EnableAutofillMetadataFieldTrial();
}
protected:
void InitFeature(base::test::ScopedFeatureList* feature_list,
const base::Feature& feature,
bool is_enabled) {
if (is_enabled)
feature_list->InitAndEnableFeature(feature);
else
feature_list->InitAndDisableFeature(feature);
}
// Single field forms are not parseable iff all of the minimum required field
// values are enforced.
void CheckFormShouldBeParsed(const char* trace_message,
const FormData form,
bool expected_if_all_enforced,
bool expected_if_not_all_enforced) {
SCOPED_TRACE(trace_message);
for (bool enforce_min_for_heuristics : {true, false}) {
base::test::ScopedFeatureList heuristics, query, upload;
InitFeature(&heuristics, kAutofillEnforceMinRequiredFieldsForHeuristics,
enforce_min_for_heuristics);
for (bool enforce_min_for_query : {true, false}) {
base::test::ScopedFeatureList heuristics, query, upload;
InitFeature(&query, kAutofillEnforceMinRequiredFieldsForQuery,
enforce_min_for_query);
for (bool enforce_min_for_upload : {true, false}) {
base::test::ScopedFeatureList heuristics, query, upload;
InitFeature(&upload, kAutofillEnforceMinRequiredFieldsForUpload,
enforce_min_for_upload);
bool all_enforced = enforce_min_for_heuristics &&
enforce_min_for_query && enforce_min_for_upload;
FormStructure form_structure(form);
if (all_enforced) {
EXPECT_EQ(expected_if_all_enforced,
form_structure.ShouldBeParsed());
} else {
EXPECT_EQ(expected_if_not_all_enforced,
form_structure.ShouldBeParsed())
<< "heuristics:" << enforce_min_for_heuristics << "; "
<< "query:" << enforce_min_for_query << "; "
<< "upload:" << enforce_min_for_upload;
}
}
}
}
}
bool FormIsAutofillable(const FormData& form, bool enforce_min_fields) {
base::test::ScopedFeatureList feature_list;
InitFeature(&feature_list, kAutofillEnforceMinRequiredFieldsForHeuristics,
enforce_min_fields);
FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
0 /* source_id */);
return form_structure.IsAutofillable();
}
bool FormShouldRunHeuristics(const FormData& form, bool enforce_min_fields) {
base::test::ScopedFeatureList feature_list;
InitFeature(&feature_list, kAutofillEnforceMinRequiredFieldsForHeuristics,
enforce_min_fields);
FormStructure form_structure(form);
return form_structure.ShouldRunHeuristics();
}
bool FormShouldBeQueried(const FormData& form, bool enforce_min_fields) {
base::test::ScopedFeatureList feature_list;
InitFeature(&feature_list, kAutofillEnforceMinRequiredFieldsForQuery,
enforce_min_fields);
FormStructure form_structure(form);
return form_structure.ShouldBeQueried();
}
bool FormShouldBeUploaded(const FormData& form, bool enforce_min_fields) {
base::test::ScopedFeatureList feature_list;
InitFeature(&feature_list, kAutofillEnforceMinRequiredFieldsForUpload,
enforce_min_fields);
FormStructure form_structure(form);
return form_structure.ShouldBeUploaded();
}
void DisableAutofillMetadataFieldTrial() { field_trial_list_.reset(); }
private:
void EnableAutofillMetadataFieldTrial() {
field_trial_list_.reset();
field_trial_list_.reset(new base::FieldTrialList(
std::make_unique<variations::SHA1EntropyProvider>("foo")));
field_trial_ = base::FieldTrialList::CreateFieldTrial(
"AutofillFieldMetadata", "Enabled");
field_trial_->group();
}
std::unique_ptr<base::FieldTrialList> field_trial_list_;
scoped_refptr<base::FieldTrial> field_trial_;
};
TEST_F(FormStructureTest, FieldCount) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.label = ASCIIToUTF16("username");
field.name = ASCIIToUTF16("username");
field.form_control_type = "text";
form.fields.push_back(field);
field.label = ASCIIToUTF16("password");
field.name = ASCIIToUTF16("password");
field.form_control_type = "password";
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("Submit");
field.form_control_type = "submit";
form.fields.push_back(field);
field.label = ASCIIToUTF16("address1");
field.name = ASCIIToUTF16("address1");
field.form_control_type = "text";
field.should_autocomplete = false;
form.fields.push_back(field);
// The render process sends all fields to browser including fields with
// autocomplete=off
form_structure.reset(new FormStructure(form));
EXPECT_EQ(4U, form_structure->field_count());
}
TEST_F(FormStructureTest, AutofillCount) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.label = ASCIIToUTF16("username");
field.name = ASCIIToUTF16("username");
field.form_control_type = "text";
form.fields.push_back(field);
field.label = ASCIIToUTF16("password");
field.name = ASCIIToUTF16("password");
field.form_control_type = "password";
form.fields.push_back(field);
field.label = ASCIIToUTF16("email");
field.name = ASCIIToUTF16("email");
field.form_control_type = "text";
form.fields.push_back(field);
field.label = ASCIIToUTF16("city");
field.name = ASCIIToUTF16("city");
field.form_control_type = "text";
form.fields.push_back(field);
field.label = ASCIIToUTF16("state");
field.name = ASCIIToUTF16("state");
field.form_control_type = "select-one";
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("Submit");
field.form_control_type = "submit";
form.fields.push_back(field);
// Only text and select fields that are heuristically matched are counted.
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_EQ(3U, form_structure->autofill_count());
// Add a field with should_autocomplete=false. This should not be considered a
// fillable field.
field.label = ASCIIToUTF16("address1");
field.name = ASCIIToUTF16("address1");
field.form_control_type = "text";
field.should_autocomplete = false;
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_EQ(4U, form_structure->autofill_count());
}
TEST_F(FormStructureTest, SourceURL) {
FormData form;
form.origin = GURL("http://www.foo.com/");
FormStructure form_structure(form);
EXPECT_EQ(form.origin, form_structure.source_url());
}
TEST_F(FormStructureTest, IsAutofillable) {
FormData form;
FormFieldData field;
// Start with a username field. It should be picked up by the password but
// not by autofill.
field.label = ASCIIToUTF16("username");
field.name = ASCIIToUTF16("username");
field.form_control_type = "text";
form.fields.push_back(field);
// With min required fields enabled.
EXPECT_FALSE(FormIsAutofillable(form, true)); // Min enforced.
EXPECT_FALSE(FormIsAutofillable(form, false)); // Min not enforced.
// Add a password field. The form should be picked up by the password but
// not by autofill.
field.label = ASCIIToUTF16("password");
field.name = ASCIIToUTF16("password");
field.form_control_type = "password";
form.fields.push_back(field);
EXPECT_FALSE(FormIsAutofillable(form, true)); // Min enforced.
EXPECT_FALSE(FormIsAutofillable(form, false)); // Min not enforced.
// Add an auto-fillable fields. With just one auto-fillable field, this should
// be picked up by autofill only if there is no minimum field enforcement.
field.label = ASCIIToUTF16("Full Name");
field.name = ASCIIToUTF16("fullname");
field.form_control_type = "text";
form.fields.push_back(field);
EXPECT_FALSE(FormIsAutofillable(form, true)); // Min enforced.
EXPECT_TRUE(FormIsAutofillable(form, false)); // Min not enforced.
// Add an auto-fillable fields. With just one auto-fillable field, this should
// be picked up by autofill only if there is no minimum field enforcement.
field.label = ASCIIToUTF16("Address Line 1");
field.name = ASCIIToUTF16("address1");
field.form_control_type = "text";
form.fields.push_back(field);
EXPECT_FALSE(FormIsAutofillable(form, true)); // Min enforced.
EXPECT_TRUE(FormIsAutofillable(form, false)); // Min not enforced.
// We now have three auto-fillable fields. It's always autofillable.
field.label = ASCIIToUTF16("Email");
field.name = ASCIIToUTF16("email");
field.form_control_type = "email";
form.fields.push_back(field);
EXPECT_TRUE(FormIsAutofillable(form, true)); // Min enforced.
EXPECT_TRUE(FormIsAutofillable(form, false)); // Min not enforced.
// The target cannot include http(s)://*/search...
form.action = GURL("http://google.com/search?q=hello");
EXPECT_FALSE(FormIsAutofillable(form, true)); // Min enforced.
EXPECT_FALSE(FormIsAutofillable(form, false)); // Min not enforced.
// But search can be in the URL.
form.action = GURL("http://search.com/?q=hello");
EXPECT_TRUE(FormIsAutofillable(form, true)); // Min enforced.
EXPECT_TRUE(FormIsAutofillable(form, false)); // Min not enforced.
}
TEST_F(FormStructureTest, ShouldBeParsed) {
FormData form;
// Start with a single checkable field.
FormFieldData checkable_field;
checkable_field.check_status = FormFieldData::CHECKABLE_BUT_UNCHECKED;
checkable_field.name = ASCIIToUTF16("radiobtn");
checkable_field.form_control_type = "radio";
form.fields.push_back(checkable_field);
// A form with a single checkable field isn't interesting.
CheckFormShouldBeParsed("one checkable", form, false, false);
// Add a second checkable field.
checkable_field.name = ASCIIToUTF16("checkbox");
checkable_field.form_control_type = "checkbox";
form.fields.push_back(checkable_field);
// A form with a only checkable fields isn't interesting.
CheckFormShouldBeParsed("two checkable", form, false, false);
// Add a text field.
FormFieldData field;
field.label = ASCIIToUTF16("username");
field.name = ASCIIToUTF16("username");
field.form_control_type = "text";
form.fields.push_back(field);
// Single text field forms shouldn't be parsed if all of the minimums are
// enforced but should be parsed if ANY of the minimums is not enforced.
CheckFormShouldBeParsed("username", form, false, true);
// We now have three text fields, though only two are auto-fillable.
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("firstname");
field.form_control_type = "text";
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("lastname");
field.form_control_type = "text";
form.fields.push_back(field);
// Three text field forms should always be parsed.
CheckFormShouldBeParsed("three field", form, true, true);
// The target cannot include http(s)://*/search...
form.action = GURL("http://google.com/search?q=hello");
CheckFormShouldBeParsed("search path", form, false, false);
// But search can be in the URL.
form.action = GURL("http://search.com/?q=hello");
CheckFormShouldBeParsed("search domain", form, true, true);
// The form need only have three fields, but at least one must be a text
// field.
form.fields.clear();
field.label = ASCIIToUTF16("Email");
field.name = ASCIIToUTF16("email");
field.form_control_type = "email";
form.fields.push_back(field);
field.label = ASCIIToUTF16("State");
field.name = ASCIIToUTF16("state");
field.form_control_type = "select-one";
form.fields.push_back(field);
field.label = ASCIIToUTF16("Country");
field.name = ASCIIToUTF16("country");
field.form_control_type = "select-one";
form.fields.push_back(field);
CheckFormShouldBeParsed("text + selects", form, true, true);
// Now, no text fields.
form.fields[0].form_control_type = "select-one";
CheckFormShouldBeParsed("only selects", form, false, false);
// We have only one field, which is password.
form.fields.clear();
field.label = ASCIIToUTF16("Password");
field.name = ASCIIToUTF16("pw");
field.form_control_type = "password";
form.fields.push_back(field);
CheckFormShouldBeParsed("password", form, false, true);
// We have two fields, which are passwords, should be parsed.
field.label = ASCIIToUTF16("New password");
field.name = ASCIIToUTF16("new_pw");
field.form_control_type = "password";
form.fields.push_back(field);
CheckFormShouldBeParsed("new password", form, true, true);
// There are 2 fields, one of which is password, and this is an upload of
// a sign-in form submission, should be parsed. even if all minimums are
// enforced.
form.fields.clear();
field.name = ASCIIToUTF16("username");
field.form_control_type = "text";
form.fields.push_back(field);
field.name = ASCIIToUTF16("pw");
field.form_control_type = "password";
form.fields.push_back(field);
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
// Enabled.
{kAutofillEnforceMinRequiredFieldsForHeuristics,
kAutofillEnforceMinRequiredFieldsForQuery,
kAutofillEnforceMinRequiredFieldsForUpload},
// Disabled.
{});
FormStructure form_structure(form);
form_structure.set_is_signin_upload(true);
EXPECT_TRUE(form_structure.ShouldBeParsed());
}
// Tests that ShouldBeParsed returns true for a form containing less than three
// fields if at least one has an autocomplete attribute.
TEST_F(FormStructureTest, ShouldBeParsed_TwoFields_HasAutocomplete) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.label = ASCIIToUTF16("Name");
field.name = ASCIIToUTF16("name");
field.form_control_type = "name";
field.autocomplete_attribute = "name";
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address");
field.name = ASCIIToUTF16("Address");
field.form_control_type = "select-one";
field.autocomplete_attribute = "";
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->ParseFieldTypesFromAutocompleteAttributes();
EXPECT_TRUE(form_structure->ShouldBeParsed());
}
// Tests that ShouldBeParsed returns true for a form containing less than three
// fields if at least one has an autocomplete attribute.
TEST_F(FormStructureTest, DetermineHeuristicTypes_AutocompleteFalse) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.label = ASCIIToUTF16("Name");
field.name = ASCIIToUTF16("name");
field.form_control_type = "text";
field.autocomplete_attribute = "false";
form.fields.push_back(field);
field.label = ASCIIToUTF16("Email");
field.name = ASCIIToUTF16("email");
field.form_control_type = "text";
field.autocomplete_attribute = "false";
form.fields.push_back(field);
field.label = ASCIIToUTF16("State");
field.name = ASCIIToUTF16("state");
field.form_control_type = "select-one";
field.autocomplete_attribute = "false";
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->ShouldBeParsed());
EXPECT_EQ(3U, form_structure->autofill_count());
EXPECT_EQ(NAME_FULL, form_structure->field(0)->Type().GetStorableType());
EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(1)->Type().GetStorableType());
EXPECT_EQ(ADDRESS_HOME_STATE,
form_structure->field(2)->Type().GetStorableType());
}
TEST_F(FormStructureTest, HeuristicsContactInfo) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("firstname");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("lastname");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Email");
field.name = ASCIIToUTF16("email");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Phone");
field.name = ASCIIToUTF16("phone");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Ext:");
field.name = ASCIIToUTF16("phoneextension");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address");
field.name = ASCIIToUTF16("address");
form.fields.push_back(field);
field.label = ASCIIToUTF16("City");
field.name = ASCIIToUTF16("city");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Zip code");
field.name = ASCIIToUTF16("zipcode");
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("Submit");
field.form_control_type = "submit";
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
ASSERT_EQ(9U, form_structure->field_count());
ASSERT_EQ(8U, form_structure->autofill_count());
// First name.
EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
// Last name.
EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
// Email.
EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(2)->heuristic_type());
// Phone.
EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER,
form_structure->field(3)->heuristic_type());
// Phone extension.
EXPECT_EQ(PHONE_HOME_EXTENSION, form_structure->field(4)->heuristic_type());
// Address.
EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(5)->heuristic_type());
// City.
EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(6)->heuristic_type());
// Zip.
EXPECT_EQ(ADDRESS_HOME_ZIP, form_structure->field(7)->heuristic_type());
// Submit.
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(8)->heuristic_type());
}
// Verify that we can correctly process the |autocomplete| attribute.
TEST_F(FormStructureTest, HeuristicsAutocompleteAttribute) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = base::string16();
field.name = ASCIIToUTF16("field1");
field.autocomplete_attribute = "given-name";
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("field2");
field.autocomplete_attribute = "family-name";
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("field3");
field.autocomplete_attribute = "email";
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("field4");
field.autocomplete_attribute = "upi-vpa";
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
EXPECT_TRUE(form_structure->has_author_specified_types());
EXPECT_TRUE(form_structure->has_author_specified_upi_vpa_hint());
// Expect the correct number of fields.
ASSERT_EQ(4U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
EXPECT_EQ(HTML_TYPE_GIVEN_NAME, form_structure->field(0)->html_type());
EXPECT_EQ(HTML_TYPE_FAMILY_NAME, form_structure->field(1)->html_type());
EXPECT_EQ(HTML_TYPE_EMAIL, form_structure->field(2)->html_type());
EXPECT_EQ(HTML_TYPE_UNRECOGNIZED, form_structure->field(3)->html_type());
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(0)->heuristic_type());
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(1)->heuristic_type());
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(2)->heuristic_type());
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(3)->heuristic_type());
}
// Verify that the heuristics are not run for non checkout formless forms.
TEST_F(FormStructureTest, Heuristics_FormlessNonCheckoutForm) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::kAutofillRestrictUnownedFieldsToFormlessCheckout);
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("First Name:");
field.name = ASCIIToUTF16("firstname");
field.autocomplete_attribute = "given-name";
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name:");
field.name = ASCIIToUTF16("lastname");
field.autocomplete_attribute = "family-name";
form.fields.push_back(field);
field.label = ASCIIToUTF16("Email:");
field.name = ASCIIToUTF16("email");
field.autocomplete_attribute = "email";
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
ASSERT_EQ(3U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
// The heuristic type should be good.
EXPECT_EQ(HTML_TYPE_GIVEN_NAME, form_structure->field(0)->html_type());
EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
// Set the form as a formless non checkout form.
form.is_formless_checkout = false;
form.is_form_tag = false;
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
ASSERT_EQ(3U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
// The heuristic type should be Unknown.
EXPECT_EQ(HTML_TYPE_GIVEN_NAME, form_structure->field(0)->html_type());
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(0)->heuristic_type());
}
// All fields share a common prefix which could confuse the heuristics. Test
// that the common prefix is stripped out before running heuristics.
TEST_F(FormStructureTest, StripCommonNamePrefix) {
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("ctl01$ctl00$ShippingAddressCreditPhone$firstname");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("ctl01$ctl00$ShippingAddressCreditPhone$lastname");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Email");
field.name = ASCIIToUTF16("ctl01$ctl00$ShippingAddressCreditPhone$email");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Phone");
field.name = ASCIIToUTF16("ctl01$ctl00$ShippingAddressCreditPhone$phone");
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("ctl01$ctl00$ShippingAddressCreditPhone$submit");
field.form_control_type = "submit";
form.fields.push_back(field);
std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
ASSERT_EQ(5U, form_structure->field_count());
ASSERT_EQ(4U, form_structure->autofill_count());
// First name.
EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
// Last name.
EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
// Email.
EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(2)->heuristic_type());
// Phone.
EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER,
form_structure->field(3)->heuristic_type());
// Submit.
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(4)->heuristic_type());
}
// All fields share a common prefix which is small enough that it is not
// stripped from the name before running the heuristics.
TEST_F(FormStructureTest, StripCommonNamePrefix_SmallPrefix) {
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Address 1");
field.name = ASCIIToUTF16("address1");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address 2");
field.name = ASCIIToUTF16("address2");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address 3");
field.name = ASCIIToUTF16("address3");
form.fields.push_back(field);
std::unique_ptr<FormStructure> form_structure(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
ASSERT_EQ(3U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
// Address 1.
EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(0)->heuristic_type());
// Address 2.
EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(1)->heuristic_type());
// Address 3
EXPECT_EQ(ADDRESS_HOME_LINE3, form_structure->field(2)->heuristic_type());
}
TEST_F(FormStructureTest, IsCompleteCreditCardForm_Minimal) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Expiration");
field.name = ASCIIToUTF16("cc_exp");
form.fields.push_back(field);
// Another field to reach the minimum 3.
field.label = ASCIIToUTF16("Zip");
field.name = ASCIIToUTF16("zip");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsCompleteCreditCardForm());
}
TEST_F(FormStructureTest, IsCompleteCreditCardForm_Full) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Name on Card");
field.name = ASCIIToUTF16("name_on_card");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Exp Month");
field.name = ASCIIToUTF16("ccmonth");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Exp Year");
field.name = ASCIIToUTF16("ccyear");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Verification");
field.name = ASCIIToUTF16("verification");
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("Submit");
field.form_control_type = "submit";
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsCompleteCreditCardForm());
}
// A form with only the credit card number is not considered sufficient.
TEST_F(FormStructureTest, IsCompleteCreditCardForm_OnlyCCNumber) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_FALSE(form_structure->IsCompleteCreditCardForm());
}
// A form with only the credit card number is not considered sufficient.
TEST_F(FormStructureTest, IsCompleteCreditCardForm_AddressForm) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("First Name");
field.name = base::string16();
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name");
field.name = base::string16();
form.fields.push_back(field);
field.label = ASCIIToUTF16("Email");
field.name = base::string16();
form.fields.push_back(field);
field.label = ASCIIToUTF16("Phone");
field.name = base::string16();
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address");
field.name = base::string16();
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address");
field.name = base::string16();
form.fields.push_back(field);
field.label = ASCIIToUTF16("Zip code");
field.name = base::string16();
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_FALSE(form_structure->IsCompleteCreditCardForm());
}
// Verify that we can correctly process the 'autocomplete' attribute for phone
// number types (especially phone prefixes and suffixes).
TEST_F(FormStructureTest, HeuristicsAutocompleteAttributePhoneTypes) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = base::string16();
field.name = ASCIIToUTF16("field1");
field.autocomplete_attribute = "tel-local";
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("field2");
field.autocomplete_attribute = "tel-local-prefix";
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("field3");
field.autocomplete_attribute = "tel-local-suffix";
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
ASSERT_EQ(3U, form_structure->field_count());
EXPECT_EQ(3U, form_structure->autofill_count());
EXPECT_EQ(HTML_TYPE_TEL_LOCAL, form_structure->field(0)->html_type());
EXPECT_EQ(AutofillField::IGNORED, form_structure->field(0)->phone_part());
EXPECT_EQ(HTML_TYPE_TEL_LOCAL_PREFIX, form_structure->field(1)->html_type());
EXPECT_EQ(AutofillField::PHONE_PREFIX,
form_structure->field(1)->phone_part());
EXPECT_EQ(HTML_TYPE_TEL_LOCAL_SUFFIX, form_structure->field(2)->html_type());
EXPECT_EQ(AutofillField::PHONE_SUFFIX,
form_structure->field(2)->phone_part());
}
// The heuristics and server predictions should run if there are more than two
// fillable fields.
TEST_F(FormStructureTest,
HeuristicsAndServerPredictions_BigForm_NoAutocompleteAttribute) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("firstname");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("lastname");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Email");
field.name = ASCIIToUTF16("email");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
EXPECT_TRUE(form_structure->ShouldBeQueried());
EXPECT_TRUE(form_structure->ShouldBeUploaded());
ASSERT_EQ(3U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(2)->heuristic_type());
}
// The heuristics and server predictions should run even if a valid autocomplete
// attribute is present in the form (if it has more that two fillable fields).
TEST_F(FormStructureTest,
HeuristicsAndServerPredictions_ValidAutocompleteAttribute) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
// Set a valid autocomplete attribute to the first field.
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("firstname");
field.autocomplete_attribute = "given-name";
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("lastname");
field.autocomplete_attribute = "";
form.fields.push_back(field);
field.label = ASCIIToUTF16("Email");
field.name = ASCIIToUTF16("email");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
EXPECT_TRUE(form_structure->ShouldBeQueried());
EXPECT_TRUE(form_structure->ShouldBeUploaded());
ASSERT_EQ(3U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(2)->heuristic_type());
}
// The heuristics and server predictions should run even if an unrecognized
// autocomplete attribute is present in the form (if it has more than two
// fillable fields).
TEST_F(FormStructureTest,
HeuristicsAndServerPredictions_UnrecognizedAutocompleteAttribute) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
// Set an unrecognized autocomplete attribute to the first field.
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("firstname");
field.autocomplete_attribute = "unrecognized";
form.fields.push_back(field);
field.label = ASCIIToUTF16("Middle Name");
field.name = ASCIIToUTF16("middlename");
field.autocomplete_attribute = "";
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("lastname");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Email");
field.name = ASCIIToUTF16("email");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
EXPECT_TRUE(form_structure->ShouldBeQueried());
ASSERT_EQ(4U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
EXPECT_EQ(NAME_MIDDLE, form_structure->field(1)->heuristic_type());
EXPECT_EQ(NAME_LAST, form_structure->field(2)->heuristic_type());
EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(3)->heuristic_type());
}
// Tests whether the heuristics and server predictions are run for forms with
// fewer than 3 fields and no autocomplete attributes.
TEST_F(FormStructureTest,
HeuristicsAndServerPredictions_SmallForm_NoAutocompleteAttribute) {
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("firstname");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("lastname");
form.fields.push_back(field);
EXPECT_FALSE(FormShouldRunHeuristics(form, true)); // Min enforced.
EXPECT_TRUE(FormShouldRunHeuristics(form, false)); // Min not enforced.
EXPECT_FALSE(FormShouldBeQueried(form, true)); // Min enforced.
EXPECT_TRUE(FormShouldBeQueried(form, false)); // Min not enforced.
// Status Quo (Q3/2017) - Small forms not supported.
{
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
// Enabled.
{kAutofillEnforceMinRequiredFieldsForHeuristics,
kAutofillEnforceMinRequiredFieldsForQuery,
kAutofillEnforceMinRequiredFieldsForUpload},
// Disabled.
{});
FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
0 /* source_id */);
ASSERT_EQ(2U, form_structure.field_count());
ASSERT_EQ(0U, form_structure.autofill_count());
EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(1)->heuristic_type());
EXPECT_EQ(NO_SERVER_DATA, form_structure.field(0)->server_type());
EXPECT_EQ(NO_SERVER_DATA, form_structure.field(1)->server_type());
EXPECT_FALSE(form_structure.IsAutofillable());
}
// Default configuration.
{
FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
0 /* source_id */);
ASSERT_EQ(2U, form_structure.field_count());
ASSERT_EQ(0U, form_structure.autofill_count());
EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(1)->heuristic_type());
EXPECT_EQ(NO_SERVER_DATA, form_structure.field(0)->server_type());
EXPECT_EQ(NO_SERVER_DATA, form_structure.field(1)->server_type());
EXPECT_FALSE(form_structure.IsAutofillable());
}
// Enable small form heuristics.
{
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
kAutofillEnforceMinRequiredFieldsForHeuristics);
FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
0 /* source_id */);
ASSERT_EQ(2U, form_structure.field_count());
ASSERT_EQ(2U, form_structure.autofill_count());
EXPECT_EQ(NAME_FIRST, form_structure.field(0)->heuristic_type());
EXPECT_EQ(NAME_LAST, form_structure.field(1)->heuristic_type());
EXPECT_EQ(NO_SERVER_DATA, form_structure.field(0)->server_type());
EXPECT_EQ(NO_SERVER_DATA, form_structure.field(1)->server_type());
EXPECT_TRUE(form_structure.IsAutofillable());
}
}
// Tests the heuristics and server predictions are not run for forms with less
// than 3 fields, if the minimum fields required feature is enforced, even if an
// autocomplete attribute is specified.
TEST_F(FormStructureTest,
HeuristicsAndServerPredictions_SmallForm_ValidAutocompleteAttribute) {
FormData form;
FormFieldData field;
field.form_control_type = "text";
// Set a valid autocompelete attribute to the first field.
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("firstname");
field.autocomplete_attribute = "given-name";
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("lastname");
field.autocomplete_attribute = "";
form.fields.push_back(field);
EXPECT_FALSE(FormShouldRunHeuristics(form, true)); // Min enforced.
EXPECT_TRUE(FormShouldRunHeuristics(form, false)); // Min not enforced.
EXPECT_FALSE(FormShouldBeQueried(form, true)); // Min enforced.
EXPECT_TRUE(FormShouldBeQueried(form, false)); // Min not enforced.
// Status Quo (Q3/2017) - Small forms not supported.
{
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
// Enabled.
{kAutofillEnforceMinRequiredFieldsForHeuristics,
kAutofillEnforceMinRequiredFieldsForQuery,
kAutofillEnforceMinRequiredFieldsForUpload},
// Disabled.
{});
FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
0 /* source_id */);
ASSERT_EQ(2U, form_structure.field_count());
ASSERT_EQ(1U, form_structure.autofill_count());
EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(1)->heuristic_type());
EXPECT_EQ(NO_SERVER_DATA, form_structure.field(0)->server_type());
EXPECT_EQ(NO_SERVER_DATA, form_structure.field(1)->server_type());
EXPECT_FALSE(form_structure.IsAutofillable());
}
// Enable small form heuristics.
{
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
kAutofillEnforceMinRequiredFieldsForHeuristics);
FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
0 /* source_id */);
ASSERT_EQ(2U, form_structure.field_count());
ASSERT_EQ(2U, form_structure.autofill_count());
EXPECT_EQ(NAME_FIRST, form_structure.field(0)->heuristic_type());
EXPECT_EQ(NAME_LAST, form_structure.field(1)->heuristic_type());
EXPECT_EQ(NO_SERVER_DATA, form_structure.field(0)->server_type());
EXPECT_EQ(NO_SERVER_DATA, form_structure.field(1)->server_type());
EXPECT_EQ(NAME_FIRST, form_structure.field(0)->Type().GetStorableType());
EXPECT_EQ(NAME_LAST, form_structure.field(1)->Type().GetStorableType());
EXPECT_TRUE(form_structure.IsAutofillable());
}
// As a side effect of parsing small forms (if any of the heuristics, query,
// or upload minimmums are disabled, we'll autofill fields with an
// autocomplete attribute, even if its the only field in the form.
{
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
kAutofillEnforceMinRequiredFieldsForUpload);
FormData form_copy = form;
form_copy.fields.pop_back();
FormStructure form_structure(form_copy);
form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
0 /* source_id */);
ASSERT_EQ(1U, form_structure.field_count());
ASSERT_EQ(1U, form_structure.autofill_count());
EXPECT_EQ(UNKNOWN_TYPE, form_structure.field(0)->heuristic_type());
EXPECT_EQ(NO_SERVER_DATA, form_structure.field(0)->server_type());
EXPECT_EQ(NAME_FIRST, form_structure.field(0)->Type().GetStorableType());
EXPECT_TRUE(form_structure.IsAutofillable());
}
}
// Even with an 'autocomplete' attribute set, ShouldBeQueried() should
// return true if the structure contains a password field, since there are
// no local heuristics to depend upon in this case. Fields will still not be
// considered autofillable though.
TEST_F(FormStructureTest, PasswordFormShouldBeQueried) {
FormData form;
// Start with a regular contact form.
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("firstname");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("lastname");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Email");
field.name = ASCIIToUTF16("email");
field.autocomplete_attribute = "username";
form.fields.push_back(field);
field.label = ASCIIToUTF16("Password");
field.name = ASCIIToUTF16("Password");
field.form_control_type = "password";
form.fields.push_back(field);
FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
0 /* source_id */);
EXPECT_TRUE(form_structure.ShouldBeQueried());
EXPECT_TRUE(form_structure.ShouldBeUploaded());
}
// Verify that we can correctly process sections listed in the |autocomplete|
// attribute.
TEST_F(FormStructureTest, HeuristicsAutocompleteAttributeWithSections) {
FormData form;
FormFieldData field;
field.form_control_type = "text";
// Some fields will have no section specified. These fall into the default
// section.
field.autocomplete_attribute = "email";
form.fields.push_back(field);
// We allow arbitrary section names.
field.autocomplete_attribute = "section-foo email";
form.fields.push_back(field);
// "shipping" and "billing" are special section tokens that don't require the
// "section-" prefix.
field.autocomplete_attribute = "shipping email";
form.fields.push_back(field);
field.autocomplete_attribute = "billing email";
form.fields.push_back(field);
// "shipping" and "billing" can be combined with other section names.
field.autocomplete_attribute = "section-foo shipping email";
form.fields.push_back(field);
field.autocomplete_attribute = "section-foo billing email";
form.fields.push_back(field);
// We don't do anything clever to try to coalesce sections; it's up to site
// authors to avoid typos.
field.autocomplete_attribute = "section--foo email";
form.fields.push_back(field);
// "shipping email" and "section--shipping" email should be parsed as
// different sections. This is only an interesting test due to how we
// implement implicit section names from attributes like "shipping email"; see
// the implementation for more details.
field.autocomplete_attribute = "section--shipping email";
form.fields.push_back(field);
// Credit card fields are implicitly in a separate section from other fields.
field.autocomplete_attribute = "section-foo cc-number";
form.fields.push_back(field);
FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
0 /* source_id */);
EXPECT_TRUE(form_structure.IsAutofillable());
// Expect the correct number of fields.
ASSERT_EQ(9U, form_structure.field_count());
EXPECT_EQ(9U, form_structure.autofill_count());
// All of the fields in this form should be parsed as belonging to different
// sections.
std::set<std::string> section_names;
for (size_t i = 0; i < 9; ++i) {
section_names.insert(form_structure.field(i)->section);
}
EXPECT_EQ(9U, section_names.size());
}
// Verify that we can correctly process a degenerate section listed in the
// |autocomplete| attribute.
TEST_F(FormStructureTest,
HeuristicsAutocompleteAttributeWithSectionsDegenerate) {
FormData form;
FormFieldData field;
field.form_control_type = "text";
// Some fields will have no section specified. These fall into the default
// section.
field.autocomplete_attribute = "email";
form.fields.push_back(field);
// Specifying "section-" is equivalent to not specifying a section.
field.autocomplete_attribute = "section- email";
form.fields.push_back(field);
// Invalid tokens should prevent us from setting a section name.
field.autocomplete_attribute = "garbage section-foo email";
form.fields.push_back(field);
field.autocomplete_attribute = "garbage section-bar email";
form.fields.push_back(field);
field.autocomplete_attribute = "garbage shipping email";
form.fields.push_back(field);
field.autocomplete_attribute = "garbage billing email";
form.fields.push_back(field);
FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
0 /* source_id */);
// Expect the correct number of fields.
ASSERT_EQ(6U, form_structure.field_count());
EXPECT_EQ(2U, form_structure.autofill_count());
// All of the fields in this form should be parsed as belonging to the same
// section.
std::set<std::string> section_names;
for (size_t i = 0; i < 6; ++i) {
section_names.insert(form_structure.field(i)->section);
}
EXPECT_EQ(1U, section_names.size());
}
// Verify that we can correctly process repeated sections listed in the
// |autocomplete| attribute.
TEST_F(FormStructureTest, HeuristicsAutocompleteAttributeWithSectionsRepeated) {
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.autocomplete_attribute = "section-foo email";
form.fields.push_back(field);
field.autocomplete_attribute = "section-foo address-line1";
form.fields.push_back(field);
FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
0 /* source_id */);
// Expect the correct number of fields.
ASSERT_EQ(2U, form_structure.field_count());
EXPECT_EQ(2U, form_structure.autofill_count());
// All of the fields in this form should be parsed as belonging to the same
// section.
std::set<std::string> section_names;
for (size_t i = 0; i < 2; ++i) {
section_names.insert(form_structure.field(i)->section);
}
EXPECT_EQ(1U, section_names.size());
}
// Verify that we do not override the author-specified sections from a form with
// local heuristics.
TEST_F(FormStructureTest, HeuristicsDontOverrideAutocompleteAttributeSections) {
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.name = ASCIIToUTF16("one");
field.autocomplete_attribute = "address-line1";
form.fields.push_back(field);
field.name = base::string16();
field.autocomplete_attribute = "section-foo email";
form.fields.push_back(field);
field.name = base::string16();
field.autocomplete_attribute = "name";
form.fields.push_back(field);
field.name = ASCIIToUTF16("two");
field.autocomplete_attribute = "address-line1";
form.fields.push_back(field);
FormStructure form_structure(form);
form_structure.DetermineHeuristicTypes(nullptr /* ukm_service */,
0 /* source_id */);
// Expect the correct number of fields.
ASSERT_EQ(4U, form_structure.field_count());
EXPECT_EQ(4U, form_structure.autofill_count());
// Normally, the two separate address fields would cause us to detect two
// separate sections; but because there is an author-specified section in this
// form, we do not apply these usual heuristics.
EXPECT_EQ(ASCIIToUTF16("one"), form_structure.field(0)->name);
EXPECT_EQ(ASCIIToUTF16("two"), form_structure.field(3)->name);
EXPECT_EQ(form_structure.field(0)->section, form_structure.field(3)->section);
}
TEST_F(FormStructureTest, HeuristicsSample8) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Your First Name:");
field.name = ASCIIToUTF16("bill.first");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Your Last Name:");
field.name = ASCIIToUTF16("bill.last");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Street Address Line 1:");
field.name = ASCIIToUTF16("bill.street1");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Street Address Line 2:");
field.name = ASCIIToUTF16("bill.street2");
form.fields.push_back(field);
field.label = ASCIIToUTF16("City");
field.name = ASCIIToUTF16("bill.city");
form.fields.push_back(field);
field.label = ASCIIToUTF16("State (U.S.):");
field.name = ASCIIToUTF16("bill.state");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Zip/Postal Code:");
field.name = ASCIIToUTF16("BillTo.PostalCode");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Country:");
field.name = ASCIIToUTF16("bill.country");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Phone Number:");
field.name = ASCIIToUTF16("BillTo.Phone");
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("Submit");
field.form_control_type = "submit";
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(10U, form_structure->field_count());
ASSERT_EQ(9U, form_structure->autofill_count());
// First name.
EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
// Last name.
EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
// Address.
EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(2)->heuristic_type());
// Address.
EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(3)->heuristic_type());
// City.
EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(4)->heuristic_type());
// State.
EXPECT_EQ(ADDRESS_HOME_STATE, form_structure->field(5)->heuristic_type());
// Zip.
EXPECT_EQ(ADDRESS_HOME_ZIP, form_structure->field(6)->heuristic_type());
// Country.
EXPECT_EQ(ADDRESS_HOME_COUNTRY, form_structure->field(7)->heuristic_type());
// Phone.
EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER,
form_structure->field(8)->heuristic_type());
// Submit.
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(9)->heuristic_type());
}
TEST_F(FormStructureTest, HeuristicsSample6) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("E-mail address");
field.name = ASCIIToUTF16("email");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Full name");
field.name = ASCIIToUTF16("name");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Company");
field.name = ASCIIToUTF16("company");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address");
field.name = ASCIIToUTF16("address");
form.fields.push_back(field);
field.label = ASCIIToUTF16("City");
field.name = ASCIIToUTF16("city");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Zip Code");
field.name = ASCIIToUTF16("Home.PostalCode");
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("Submit");
field.value = ASCIIToUTF16("continue");
field.form_control_type = "submit";
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(7U, form_structure->field_count());
ASSERT_EQ(6U, form_structure->autofill_count());
// Email.
EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(0)->heuristic_type());
// Full name.
EXPECT_EQ(NAME_FULL, form_structure->field(1)->heuristic_type());
// Company
EXPECT_EQ(COMPANY_NAME, form_structure->field(2)->heuristic_type());
// Address.
EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(3)->heuristic_type());
// City.
EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(4)->heuristic_type());
// Zip.
EXPECT_EQ(ADDRESS_HOME_ZIP, form_structure->field(5)->heuristic_type());
// Submit.
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(6)->heuristic_type());
}
// Tests a sequence of FormFields where only labels are supplied to heuristics
// for matching. This works because FormFieldData labels are matched in the
// case that input element ids (or |name| fields) are missing.
TEST_F(FormStructureTest, HeuristicsLabelsOnly) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("First Name");
field.name = base::string16();
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name");
field.name = base::string16();
form.fields.push_back(field);
field.label = ASCIIToUTF16("Email");
field.name = base::string16();
form.fields.push_back(field);
field.label = ASCIIToUTF16("Phone");
field.name = base::string16();
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address");
field.name = base::string16();
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address");
field.name = base::string16();
form.fields.push_back(field);
field.label = ASCIIToUTF16("Zip code");
field.name = base::string16();
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("Submit");
field.form_control_type = "submit";
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(8U, form_structure->field_count());
ASSERT_EQ(7U, form_structure->autofill_count());
// First name.
EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
// Last name.
EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
// Email.
EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(2)->heuristic_type());
// Phone.
EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER,
form_structure->field(3)->heuristic_type());
// Address.
EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(4)->heuristic_type());
// Address Line 2.
EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(5)->heuristic_type());
// Zip.
EXPECT_EQ(ADDRESS_HOME_ZIP, form_structure->field(6)->heuristic_type());
// Submit.
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(7)->heuristic_type());
}
TEST_F(FormStructureTest, HeuristicsCreditCardInfo) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Name on Card");
field.name = ASCIIToUTF16("name_on_card");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Exp Month");
field.name = ASCIIToUTF16("ccmonth");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Exp Year");
field.name = ASCIIToUTF16("ccyear");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Verification");
field.name = ASCIIToUTF16("verification");
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("Submit");
field.form_control_type = "submit";
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(6U, form_structure->field_count());
ASSERT_EQ(5U, form_structure->autofill_count());
// Credit card name.
EXPECT_EQ(CREDIT_CARD_NAME_FULL, form_structure->field(0)->heuristic_type());
// Credit card number.
EXPECT_EQ(CREDIT_CARD_NUMBER, form_structure->field(1)->heuristic_type());
// Credit card expiration month.
EXPECT_EQ(CREDIT_CARD_EXP_MONTH, form_structure->field(2)->heuristic_type());
// Credit card expiration year.
EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
form_structure->field(3)->heuristic_type());
// CVV.
EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE,
form_structure->field(4)->heuristic_type());
// Submit.
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(5)->heuristic_type());
}
TEST_F(FormStructureTest, HeuristicsCreditCardInfoWithUnknownCardField) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Name on Card");
field.name = ASCIIToUTF16("name_on_card");
form.fields.push_back(field);
// This is not a field we know how to process. But we should skip over it
// and process the other fields in the card block.
field.label = ASCIIToUTF16("Card image");
field.name = ASCIIToUTF16("card_image");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Exp Month");
field.name = ASCIIToUTF16("ccmonth");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Exp Year");
field.name = ASCIIToUTF16("ccyear");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Verification");
field.name = ASCIIToUTF16("verification");
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("Submit");
field.form_control_type = "submit";
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(7U, form_structure->field_count());
ASSERT_EQ(5U, form_structure->autofill_count());
// Credit card name.
EXPECT_EQ(CREDIT_CARD_NAME_FULL, form_structure->field(0)->heuristic_type());
// Credit card type. This is an unknown type but related to the credit card.
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(1)->heuristic_type());
// Credit card number.
EXPECT_EQ(CREDIT_CARD_NUMBER, form_structure->field(2)->heuristic_type());
// Credit card expiration month.
EXPECT_EQ(CREDIT_CARD_EXP_MONTH, form_structure->field(3)->heuristic_type());
// Credit card expiration year.
EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
form_structure->field(4)->heuristic_type());
// CVV.
EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE,
form_structure->field(5)->heuristic_type());
// Submit.
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(6)->heuristic_type());
}
TEST_F(FormStructureTest, ThreeAddressLines) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Address Line1");
field.name = ASCIIToUTF16("Address");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address Line2");
field.name = ASCIIToUTF16("Address");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address Line3");
field.name = ASCIIToUTF16("Address");
form.fields.push_back(field);
field.label = ASCIIToUTF16("City");
field.name = ASCIIToUTF16("city");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(4U, form_structure->field_count());
ASSERT_EQ(4U, form_structure->autofill_count());
// Address Line 1.
EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(0)->heuristic_type());
// Address Line 2.
EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(1)->heuristic_type());
// Address Line 3.
EXPECT_EQ(ADDRESS_HOME_LINE3, form_structure->field(2)->heuristic_type());
// City.
EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(3)->heuristic_type());
}
// Numbered address lines after line two are ignored.
TEST_F(FormStructureTest, SurplusAddressLinesIgnored) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Address Line1");
field.name = ASCIIToUTF16("shipping.address.addressLine1");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address Line2");
field.name = ASCIIToUTF16("shipping.address.addressLine2");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address Line3");
field.name = ASCIIToUTF16("billing.address.addressLine3");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address Line4");
field.name = ASCIIToUTF16("billing.address.addressLine4");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
ASSERT_EQ(4U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
// Address Line 1.
EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(0)->heuristic_type());
// Address Line 2.
EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(1)->heuristic_type());
// Address Line 3.
EXPECT_EQ(ADDRESS_HOME_LINE3, form_structure->field(2)->heuristic_type());
// Address Line 4 (ignored).
EXPECT_EQ(UNKNOWN_TYPE, form_structure->field(3)->heuristic_type());
}
// This example comes from expedia.com where they used to use a "Suite" label
// to indicate a suite or apartment number (the form has changed since this
// test was written). We interpret this as address line 2. And the following
// "Street address second line" we interpret as address line 3.
// See http://crbug.com/48197 for details.
TEST_F(FormStructureTest, ThreeAddressLinesExpedia) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Street:");
field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_ads1");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Suite or Apt:");
field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_adap");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Street address second line");
field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_ads2");
form.fields.push_back(field);
field.label = ASCIIToUTF16("City:");
field.name = ASCIIToUTF16("FOPIH_RgWebCC_0_IHAddress_adct");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(4U, form_structure->field_count());
EXPECT_EQ(4U, form_structure->autofill_count());
// Address Line 1.
EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(0)->heuristic_type());
// Suite / Apt.
EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(1)->heuristic_type());
// Address Line 3.
EXPECT_EQ(ADDRESS_HOME_LINE3, form_structure->field(2)->heuristic_type());
// City.
EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(3)->heuristic_type());
}
// This example comes from ebay.com where the word "suite" appears in the label
// and the name "address2" clearly indicates that this is the address line 2.
// See http://crbug.com/48197 for details.
TEST_F(FormStructureTest, TwoAddressLinesEbay) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Address Line1");
field.name = ASCIIToUTF16("address1");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Floor number, suite number, etc");
field.name = ASCIIToUTF16("address2");
form.fields.push_back(field);
field.label = ASCIIToUTF16("City:");
field.name = ASCIIToUTF16("city");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(3U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
// Address Line 1.
EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(0)->heuristic_type());
// Address Line 2.
EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(1)->heuristic_type());
// City.
EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(2)->heuristic_type());
}
TEST_F(FormStructureTest, HeuristicsStateWithProvince) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Address Line1");
field.name = ASCIIToUTF16("Address");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address Line2");
field.name = ASCIIToUTF16("Address");
form.fields.push_back(field);
field.label = ASCIIToUTF16("State/Province/Region");
field.name = ASCIIToUTF16("State");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(3U, form_structure->field_count());
ASSERT_EQ(3U, form_structure->autofill_count());
// Address Line 1.
EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(0)->heuristic_type());
// Address Line 2.
EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(1)->heuristic_type());
// State.
EXPECT_EQ(ADDRESS_HOME_STATE, form_structure->field(2)->heuristic_type());
}
// This example comes from lego.com's checkout page.
TEST_F(FormStructureTest, HeuristicsWithBilling) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("First Name*:");
field.name = ASCIIToUTF16("editBillingAddress$firstNameBox");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last Name*:");
field.name = ASCIIToUTF16("editBillingAddress$lastNameBox");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Company Name:");
field.name = ASCIIToUTF16("editBillingAddress$companyBox");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address*:");
field.name = ASCIIToUTF16("editBillingAddress$addressLine1Box");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Apt/Suite :");
field.name = ASCIIToUTF16("editBillingAddress$addressLine2Box");
form.fields.push_back(field);
field.label = ASCIIToUTF16("City*:");
field.name = ASCIIToUTF16("editBillingAddress$cityBox");
form.fields.push_back(field);
field.label = ASCIIToUTF16("State/Province*:");
field.name = ASCIIToUTF16("editBillingAddress$stateDropDown");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Country*:");
field.name = ASCIIToUTF16("editBillingAddress$countryDropDown");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Postal Code*:");
field.name = ASCIIToUTF16("editBillingAddress$zipCodeBox");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Phone*:");
field.name = ASCIIToUTF16("editBillingAddress$phoneBox");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Email Address*:");
field.name = ASCIIToUTF16("email$emailBox");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(11U, form_structure->field_count());
ASSERT_EQ(11U, form_structure->autofill_count());
EXPECT_EQ(NAME_FIRST, form_structure->field(0)->heuristic_type());
EXPECT_EQ(NAME_LAST, form_structure->field(1)->heuristic_type());
EXPECT_EQ(COMPANY_NAME, form_structure->field(2)->heuristic_type());
EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(3)->heuristic_type());
EXPECT_EQ(ADDRESS_HOME_LINE2, form_structure->field(4)->heuristic_type());
EXPECT_EQ(ADDRESS_HOME_CITY, form_structure->field(5)->heuristic_type());
EXPECT_EQ(ADDRESS_HOME_STATE, form_structure->field(6)->heuristic_type());
EXPECT_EQ(ADDRESS_HOME_COUNTRY, form_structure->field(7)->heuristic_type());
EXPECT_EQ(ADDRESS_HOME_ZIP, form_structure->field(8)->heuristic_type());
EXPECT_EQ(PHONE_HOME_WHOLE_NUMBER,
form_structure->field(9)->heuristic_type());
EXPECT_EQ(EMAIL_ADDRESS, form_structure->field(10)->heuristic_type());
}
TEST_F(FormStructureTest, ThreePartPhoneNumber) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Phone:");
field.name = ASCIIToUTF16("dayphone1");
field.max_length = 0;
form.fields.push_back(field);
field.label = ASCIIToUTF16("-");
field.name = ASCIIToUTF16("dayphone2");
field.max_length = 3; // Size of prefix is 3.
form.fields.push_back(field);
field.label = ASCIIToUTF16("-");
field.name = ASCIIToUTF16("dayphone3");
field.max_length = 4; // Size of suffix is 4. If unlimited size is
// passed, phone will be parsed as
// <country code> - <area code> - <phone>.
form.fields.push_back(field);
field.label = ASCIIToUTF16("ext.:");
field.name = ASCIIToUTF16("dayphone4");
field.max_length = 0;
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
ASSERT_EQ(4U, form_structure->field_count());
ASSERT_EQ(4U, form_structure->autofill_count());
// Area code.
EXPECT_EQ(PHONE_HOME_CITY_CODE, form_structure->field(0)->heuristic_type());
// Phone number suffix.
EXPECT_EQ(PHONE_HOME_NUMBER, form_structure->field(1)->heuristic_type());
// Phone number suffix.
EXPECT_EQ(PHONE_HOME_NUMBER, form_structure->field(2)->heuristic_type());
// Phone extension.
EXPECT_EQ(PHONE_HOME_EXTENSION, form_structure->field(3)->heuristic_type());
}
TEST_F(FormStructureTest, HeuristicsInfernoCC) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Name on Card");
field.name = ASCIIToUTF16("name_on_card");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address");
field.name = ASCIIToUTF16("billing_address");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Expiration Date");
field.name = ASCIIToUTF16("expiration_month");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Expiration Year");
field.name = ASCIIToUTF16("expiration_year");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
ASSERT_EQ(5U, form_structure->field_count());
EXPECT_EQ(5U, form_structure->autofill_count());
// Name on Card.
EXPECT_EQ(CREDIT_CARD_NAME_FULL, form_structure->field(0)->heuristic_type());
// Address.
EXPECT_EQ(ADDRESS_HOME_LINE1, form_structure->field(1)->heuristic_type());
// Card Number.
EXPECT_EQ(CREDIT_CARD_NUMBER, form_structure->field(2)->heuristic_type());
// Expiration Date.
EXPECT_EQ(CREDIT_CARD_EXP_MONTH, form_structure->field(3)->heuristic_type());
// Expiration Year.
EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
form_structure->field(4)->heuristic_type());
}
// Tests that the heuristics detect split credit card names if they appear in
// the middle of the form.
TEST_F(FormStructureTest, HeuristicsInferCCNames_NamesNotFirst) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Card number");
field.name = ASCIIToUTF16("ccnumber");
form.fields.push_back(field);
field.label = ASCIIToUTF16("First name");
field.name = ASCIIToUTF16("first_name");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last name");
field.name = ASCIIToUTF16("last_name");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Expiration date");
field.name = ASCIIToUTF16("ccexpiresmonth");
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("ccexpiresyear");
form.fields.push_back(field);
field.label = ASCIIToUTF16("cvc number");
field.name = ASCIIToUTF16("csc");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
ASSERT_EQ(6U, form_structure->field_count());
ASSERT_EQ(6U, form_structure->autofill_count());
// Card Number.
EXPECT_EQ(CREDIT_CARD_NUMBER, form_structure->field(0)->heuristic_type());
// First name.
EXPECT_EQ(CREDIT_CARD_NAME_FIRST, form_structure->field(1)->heuristic_type());
// Last name.
EXPECT_EQ(CREDIT_CARD_NAME_LAST, form_structure->field(2)->heuristic_type());
// Expiration Date.
EXPECT_EQ(CREDIT_CARD_EXP_MONTH, form_structure->field(3)->heuristic_type());
// Expiration Year.
EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
form_structure->field(4)->heuristic_type());
// CVC code.
EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE,
form_structure->field(5)->heuristic_type());
}
// Tests that the heuristics detect split credit card names if they appear at
// the beginning of the form. The first name has to contains some credit card
// keyword.
TEST_F(FormStructureTest, HeuristicsInferCCNames_NamesFirst) {
std::unique_ptr<FormStructure> form_structure;
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Cardholder Name");
field.name = ASCIIToUTF16("cc_first_name");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Last name");
field.name = ASCIIToUTF16("last_name");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Card number");
field.name = ASCIIToUTF16("ccnumber");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Expiration date");
field.name = ASCIIToUTF16("ccexpiresmonth");
form.fields.push_back(field);
field.label = base::string16();
field.name = ASCIIToUTF16("ccexpiresyear");
form.fields.push_back(field);
field.label = ASCIIToUTF16("cvc number");
field.name = ASCIIToUTF16("csc");
form.fields.push_back(field);
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
EXPECT_TRUE(form_structure->IsAutofillable());
// Expect the correct number of fields.
ASSERT_EQ(6U, form_structure->field_count());
ASSERT_EQ(6U, form_structure->autofill_count());
// First name.
EXPECT_EQ(CREDIT_CARD_NAME_FIRST, form_structure->field(0)->heuristic_type());
// Last name.
EXPECT_EQ(CREDIT_CARD_NAME_LAST, form_structure->field(1)->heuristic_type());
// Card Number.
EXPECT_EQ(CREDIT_CARD_NUMBER, form_structure->field(2)->heuristic_type());
// Expiration Date.
EXPECT_EQ(CREDIT_CARD_EXP_MONTH, form_structure->field(3)->heuristic_type());
// Expiration Year.
EXPECT_EQ(CREDIT_CARD_EXP_4_DIGIT_YEAR,
form_structure->field(4)->heuristic_type());
// CVC code.
EXPECT_EQ(CREDIT_CARD_VERIFICATION_CODE,
form_structure->field(5)->heuristic_type());
}
TEST_F(FormStructureTest, EncodeQueryRequest) {
FormData form;
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("Name on Card");
field.name = ASCIIToUTF16("name_on_card");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Address");
field.name = ASCIIToUTF16("billing_address");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Card Number");
field.name = ASCIIToUTF16("card_number");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Expiration Date");
field.name = ASCIIToUTF16("expiration_month");
form.fields.push_back(field);
field.label = ASCIIToUTF16("Expiration Year");
field.name = ASCIIToUTF16("expiration_year");
form.fields.push_back(field);
// Add checkable field.
FormFieldData checkable_field;
checkable_field.check_status = FormFieldData::CHECKABLE_BUT_UNCHECKED;
checkable_field.label = ASCIIToUTF16("Checkable1");
checkable_field.name = ASCIIToUTF16("Checkable1");
form.fields.push_back(checkable_field);
FormStructure form_structure(form);
std::vector<FormStructure*> forms;
forms.push_back(&form_structure);
std::vector<std::string> encoded_signatures;
// Prepare the expected proto string.
AutofillQueryContents query;
query.set_client_version("6.1.1715.1442/en (GGLL)");
AutofillQueryContents::Form* query_form = query.add_form();
query_form->set_signature(11337937696949187602U);
test::FillQueryField(query_form->add_field(), 412125936U, "name_on_card",
"text");
test::FillQueryField(query_form->add_field(), 1917667676U, "billing_address",
"text");
test::FillQueryField(query_form->add_field(), 2226358947U, "card_number",
"text");
test::FillQueryField(query_form->add_field(), 747221617U, "expiration_month",
"text");
test::FillQueryField(query_form->add_field(), 4108155786U, "expiration_year",
"text");
std::string expected_query_string;
ASSERT_TRUE(query.SerializeToString(&expected_query_string));
const char kSignature1[] = "11337937696949187602";
AutofillQueryContents encoded_query;
ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures,
&encoded_query));
ASSERT_EQ(1U, encoded_signatures.size());
EXPECT_EQ(kSignature1, encoded_signatures[0]);
std::string encoded_query_string;
encoded_query.SerializeToString(&encoded_query_string);
EXPECT_EQ(expected_query_string, encoded_query_string);
// Add the same form, only one will be encoded, so EncodeQueryRequest() should
// return the same data.
FormStructure form_structure2(form);
forms.push_back(&form_structure2);
AutofillQueryContents encoded_query2;
ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures,
&encoded_query2));
ASSERT_EQ(1U, encoded_signatures.size());
EXPECT_EQ(kSignature1, encoded_signatures[0]);
encoded_query2.SerializeToString(&encoded_query_string);
EXPECT_EQ(expected_query_string, encoded_query_string);
// Add 5 address fields - this should be still a valid form.
for (size_t i = 0; i < 5; ++i) {
field.label = ASCIIToUTF16("Address");
field.name = ASCIIToUTF16("address");
form.fields.push_back(field);
}
FormStructure form_structure3(form);
forms.push_back(&form_structure3);
// Add the second form to the expected proto.
query_form = query.add_form();
query_form->set_signature(8308881815906226214U);
test::FillQueryField(query_form->add_field(), 412125936U, "name_on_card",
"text");
test::FillQueryField(query_form->add_field(), 1917667676U, "billing_address",
"text");
test::FillQueryField(query_form->add_field(), 2226358947U, "card_number",
"text");
test::FillQueryField(query_form->add_field(), 747221617U, "expiration_month",
"text");
test::FillQueryField(query_form->add_field(), 4108155786U, "expiration_year",
"text");
for (int i = 0; i < 5; ++i) {
test::FillQueryField(query_form->add_field(), 509334676U, "address",
"text");
}
ASSERT_TRUE(query.SerializeToString(&expected_query_string));
AutofillQueryContents encoded_query3;
ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures,
&encoded_query3));
ASSERT_EQ(2U, encoded_signatures.size());
EXPECT_EQ(kSignature1, encoded_signatures[0]);
const char kSignature2[] = "8308881815906226214";
EXPECT_EQ(kSignature2, encoded_signatures[1]);
encoded_query3.SerializeToString(&encoded_query_string);
EXPECT_EQ(expected_query_string, encoded_query_string);
FormData malformed_form(form);
// Add 150 address fields - the form is not valid anymore, but previous ones
// are. The result should be the same as in previous test.
for (size_t i = 0; i < 150; ++i) {
field.label = ASCIIToUTF16("Address");
field.name = ASCIIToUTF16("address");
malformed_form.fields.push_back(field);
}
FormStructure malformed_form_structure(malformed_form);
forms.push_back(&malformed_form_structure);
AutofillQueryContents encoded_query4;
ASSERT_TRUE(FormStructure::EncodeQueryRequest(forms, &encoded_signatures,
&encoded_query4));
ASSERT_EQ(2U, encoded_signatures.size());
EXPECT_EQ(kSignature1, encoded_signatures[0]);
EXPECT_EQ(kSignature2, encoded_signatures[1]);
encoded_query4.SerializeToString(&encoded_query_string);
EXPECT_EQ(expected_query_string, encoded_query_string);
// Check that we fail if there are only bad form(s).
std::vector<FormStructure*> bad_forms;
bad_forms.push_back(&malformed_form_structure);
AutofillQueryContents encoded_query5;
EXPECT_FALSE(FormStructure::EncodeQueryRequest(bad_forms, &encoded_signatures,
&encoded_query5));
}
TEST_F(FormStructureTest, EncodeUploadRequest) {
std::unique_ptr<FormStructure> form_structure;
std::vector<ServerFieldTypeSet> possible_field_types;
FormData form;
form_structure.reset(new FormStructure(form));
form_structure->DetermineHeuristicTypes(/*ukm_service=*/nullptr,
/*source_id=*/0);
FormFieldData field;
field.form_control_type = "text";
field.label = ASCIIToUTF16("First Name");
field.name = ASCIIToUTF16("firstname");
form.fields.push_back(field);
possible_field_types.push_back(ServerFieldTypeSet());
possible_field_types.back().insert(NAME_FIRST);
field.label = ASCIIToUTF16("Last Name");
field.name = ASCIIToUTF16("lastname");
form.fields.push_back(field);
possible_field_types.push_back(ServerFieldTypeSet());
possible_field_types.back().insert(NAME_LAST);
field.label = ASCIIToUTF16("Email");
field.name = ASCIIToUTF16("email");
field.form_control_type = "email";
form.fields.push_back(field);
possible_field_types.push_back(ServerFieldTypeSet());
possible_field_types.back().insert(EMAIL_ADDRESS);
field.label = ASCIIToUTF16("Phone");
field.name = ASCIIToUTF16("phone");
field.form_control_type = "number";
form.fields.push_back(field);
possible_field_types.push_back(ServerFieldTypeSet());
possible_field_types.back().insert(PHONE_HOME_WHOLE_NUMBER);
field.label = ASCIIToUTF16("Country");
field.name = ASCIIToUTF16("country");
field.form_control_type = "select-one";
form.fields.push_back(field);
possible_field_types.push_back(ServerFieldTypeSet());
possible_field_types.back().insert(ADDRESS_HOME_COUNTRY);
// Add checkable field.
FormFieldData checkable_field;
checkable_field.check_status = FormFieldData::CHECKABLE_BUT_UNCHECKED;
checkable_field.label = ASCIIToUTF16("Checkable1");
checkable_field.name = ASCIIToUTF16("Checkable1");
form.fields.push_back(checkable_field);
possible_field_types.push_back(ServerFieldTypeSet());
possible_field_types.back().insert(ADDRESS_HOME_COUNTRY);
form_structure.reset(new FormStructure(form));
form_structure->set_password_attributes_vote(
std::make_pair(PasswordAttribute::kHasNumeric, true));
form_structure->set_password_length_vote(10u);
ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
for (size_t i = 0; i < form_structure->field_count(); ++i)
form_structure->field(i)->set_possible_types(possible_field_types[i]);
ServerFieldTypeSet available_field_types;
available_field_types.insert(NAME_FIRST);
available_field_types.insert(NAME_LAST);
available_field_types.insert(ADDRESS_HOME_LINE1);
available_field_types.insert(ADDRESS_HOME_LINE2);
available_field_types.insert(ADDRESS_HOME_COUNTRY);
available_field_types.insert(ADDRESS_BILLING_LINE1);
available_field_types.insert(ADDRESS_BILLING_LINE2);
available_field_types.insert(EMAIL_ADDRESS);
available_field_types.insert(PHONE_HOME_WHOLE_NUMBER);
// Prepare the expected proto string.
AutofillUploadContents upload;
upload.set_submission(true);
upload.set_client_version("6.1.1715.1442/en (GGLL)");
upload.set_form_signature(8736493185895608956U);
upload.set_autofill_used(false);
upload.set_data_present("144200030e");
upload.set_passwords_revealed(false);
upload.set_password_has_numeric(true);
upload.set_password_length(10u);
upload.set_action_signature(15724779818122431245U);
test::FillUploadField(upload.add_field(), 3763331450U, "firstname", "text",
nullptr, 3U);
test::FillUploadField(upload.add_field(), 3494530716U, "lastname", "text",
nullptr, 5U);
test::FillUploadField(upload.add_field(), 1029417091U, "email", "email",
nullptr, 9U);
test::FillUploadField(upload.add_field(), 466116101U, "phone", "number",
nullptr, 14U);
test::FillUploadField(upload.add_field(), 2799270304U, "country",
"select-one", nullptr, 36U);
std::string expected_upload_string;
ASSERT_TRUE(upload.SerializeToString(&expected_upload_string));
AutofillUploadContents encoded_upload;
EXPECT_TRUE(form_structure->EncodeUploadRequest(
available_field_types, false, std::string(), true, &encoded_upload));
std::string encoded_upload_string;
encoded_upload.SerializeToString(&encoded_upload_string);
EXPECT_EQ(expected_upload_string, encoded_upload_string);
// Set the "autofillused" attribute to true.
upload.set_autofill_used(true);
ASSERT_TRUE(upload.SerializeToString(&expected_upload_string));
AutofillUploadContents encoded_upload2;
EXPECT_TRUE(form_structure->EncodeUploadRequest(
available_field_types, true, std::string(), true, &encoded_upload2));
encoded_upload2.SerializeToString(&encoded_upload_string);
EXPECT_EQ(expected_upload_string, encoded_upload_string);
// Add 2 address fields - this should be still a valid form.
for (size_t i = 0; i < 2; ++i) {
field.label = ASCIIToUTF16("Address");
field.name = ASCIIToUTF16("address");
field.form_control_type = "text";
form.fields.push_back(field);
possible_field_types.push_back(ServerFieldTypeSet());
possible_field_types.back().insert(ADDRESS_HOME_LINE1);
possible_field_types.back().insert(ADDRESS_HOME_LINE2);
possible_field_types.back().insert(ADDRESS_BILLING_LINE1);
possible_field_types.back().insert(ADDRESS_BILLING_LINE2);
}
form_structure.reset(new FormStructure(form));
form_structure->set_password_attributes_vote(
std::make_pair(PasswordAttribute::kHasNumeric, true));
form_structure->set_password_length_vote(10u);
ASSERT_EQ(form_structure->field_count(), possible_field_types.size());
for (size_t i = 0; i < form_structure->field_count(); ++i)
form_structure->field(i)->set_possible_types(possible_field_types[i]);
// Adjust the expected proto string.
upload.set_form_signature(7816485729218079147U);
upload.set_autofill_used(false);
// Create an additonal 8 fields (total of 13).
for (int i = 0; i < 8; ++i) {
test::FillUploadField(upload.add_field(), 509334676U, "address", "text",
nullptr, 30U);
}
// Put the appropriate autofill type on the different address fields.
upload.mutable_field(6)->set_autofill_type(31U);
upload.mutable_field(7)->set_autofill_type(37U);
upload.mutable_field(8)->set_autofill_type(38U);
upload.mutable_field(10)->set_autofill_type(31U);
upload.mutable_field(11)->set_autofill_type(37U);
upload.mutable_field(12)->set_autofill_type(38U);
ASSERT_TRUE(upload.SerializeToString(&expected_upload_string));
AutofillUploadContents encoded_upload3;
EXPECT_TRUE(form_structure->EncodeUploadRequest(
available_field_types, false, std::string(), true, &encoded_upload3));
encoded_upload3.SerializeToString(&encoded_upload_string);
EXPECT_EQ(expected_upload_string, encoded_upload_string);
// Add 150 address fields - now the form is invalid, as it has too many
// fields.
for (size_t i = 0; i < 150; ++i) {
field.label = ASCIIToUTF16("Address");
field.name = ASCIIToUTF16("address");
field.form_control_type = "text";
form.fields.push_back(field);
possible_field_types.push_back(ServerFieldTypeSet());
possible_field_types.back().insert(ADDRESS_HOME_LINE1);
possible_field_types.back().insert(ADDRESS_HOME_LINE2);
possible_field_types.back().insert(ADDRESS_BILLING_LINE1);
possible_field_types.back().insert(ADDRESS_BILLING_LINE2);
}
form_structure.reset(new FormStructure(form));
ASSERT_EQ(form_structure->field_count(), possible_field_types.size());