| Index: components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
|
| diff --git a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
|
| index c38ff86a3fbdba7b71be36986466b9fef49efa81..57ac2053d9ba42c0655fb17b7e72a8ea5814e719 100644
|
| --- a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
|
| +++ b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
|
| @@ -39,11 +39,11 @@ class PasswordFormBuilder {
|
| // |name_and_id|, |value|, and |autocomplete| attributes. The |autocomplete|
|
| // argument can take two special values, namely:
|
| // 1.) NULL, causing no autocomplete attribute to be added,
|
| - // 2.) "", causing an empty attribute (i.e. autocomplete='') to be added.
|
| + // 2.) "", causing an empty attribute (i.e. autocomplete="") to be added.
|
| void AddUsernameField(const char* name_and_id,
|
| const char* value,
|
| const char* autocomplete) {
|
| - std::string autocomplete_attribute(autocomplete != NULL ?
|
| + std::string autocomplete_attribute(autocomplete ?
|
| base::StringPrintf("autocomplete=\"%s\"", autocomplete) : "");
|
| base::StringAppendF(
|
| &html_,
|
| @@ -52,12 +52,17 @@ class PasswordFormBuilder {
|
| }
|
|
|
| // Appends a new password-type field at the end of the form, having the
|
| - // specified |name_and_id| and |value| attributes.
|
| - void AddPasswordField(const char* name_and_id, const char* value) {
|
| + // specified |name_and_id|, |value|, and |autocomplete| attributes. Special
|
| + // values for |autocomplete| are the same as in AddUsernameField.
|
| + void AddPasswordField(const char* name_and_id,
|
| + const char* value,
|
| + const char* autocomplete) {
|
| + std::string autocomplete_attribute(autocomplete ?
|
| + base::StringPrintf("autocomplete=\"%s\"", autocomplete): "");
|
| base::StringAppendF(
|
| &html_,
|
| - "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\"/>",
|
| - name_and_id, name_and_id, value);
|
| + "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>",
|
| + name_and_id, name_and_id, value, autocomplete_attribute.c_str());
|
| }
|
|
|
| // Appends a new submit-type field at the end of the form.
|
| @@ -111,7 +116,7 @@ TEST_F(PasswordFormConversionUtilsTest, ValidWebFormElementToPasswordForm) {
|
| PasswordFormBuilder builder(kTestFormActionURL);
|
| builder.AddUsernameField("username", "johnsmith", NULL);
|
| builder.AddSubmitButton();
|
| - builder.AddPasswordField("password", "secret");
|
| + builder.AddPasswordField("password", "secret", NULL);
|
| std::string html = builder.ProduceHTML();
|
|
|
| scoped_ptr<PasswordForm> password_form;
|
| @@ -135,7 +140,7 @@ TEST_F(PasswordFormConversionUtilsTest, InvalidWebFormElementToPasswordForm) {
|
| PasswordFormBuilder builder("invalid_target");
|
| builder.AddUsernameField("username", "johnsmith", NULL);
|
| builder.AddSubmitButton();
|
| - builder.AddPasswordField("password", "secret");
|
| + builder.AddPasswordField("password", "secret", NULL);
|
| std::string html = builder.ProduceHTML();
|
|
|
| scoped_ptr<PasswordForm> password_form;
|
| @@ -147,11 +152,11 @@ TEST_F(PasswordFormConversionUtilsTest,
|
| WebFormWithMultipleUseNameAndPassWordFieldsToPasswordForm) {
|
| PasswordFormBuilder builder(kTestFormActionURL);
|
| builder.AddUsernameField("username1", "John", NULL);
|
| - builder.AddPasswordField("password1", "oldsecret");
|
| + builder.AddPasswordField("password1", "oldsecret", NULL);
|
| builder.AddUsernameField("username2", "William", NULL);
|
| - builder.AddPasswordField("password2", "secret");
|
| + builder.AddPasswordField("password2", "secret", NULL);
|
| builder.AddUsernameField("username3", "Smith", NULL);
|
| - builder.AddPasswordField("password3", "secret");
|
| + builder.AddPasswordField("password3", "secret", NULL);
|
| builder.AddSubmitButton();
|
| std::string html = builder.ProduceHTML();
|
|
|
| @@ -185,9 +190,9 @@ TEST_F(PasswordFormConversionUtilsTest,
|
| WebFormwithThreeDifferentPasswordsToPasswordForm) {
|
| PasswordFormBuilder builder(kTestFormActionURL);
|
| builder.AddUsernameField("username1", "John", NULL);
|
| - builder.AddPasswordField("password1", "alpha");
|
| - builder.AddPasswordField("password2", "beta");
|
| - builder.AddPasswordField("password3", "gamma");
|
| + builder.AddPasswordField("password1", "alpha", NULL);
|
| + builder.AddPasswordField("password2", "beta", NULL);
|
| + builder.AddPasswordField("password3", "gamma", NULL);
|
| builder.AddSubmitButton();
|
| std::string html = builder.ProduceHTML();
|
|
|
| @@ -218,12 +223,12 @@ TEST_F(PasswordFormConversionUtilsTest,
|
| {{"username", NULL, "username"}, "username1", "John", "Smith"},
|
| {{"username", "username", "username"}, "username1", "John",
|
| "William+Smith"},
|
| - // When there is an empty autocomplete attribute (i.e. autocomplete=''),
|
| + // When there is an empty autocomplete attribute (i.e. autocomplete=""),
|
| // it should have the same effect as having no attribute whatsoever.
|
| {{"", "", ""}, "username2", "William", "John+Smith"},
|
| {{"", "", "username"}, "username3", "Smith", ""},
|
| {{"username", "", "username"}, "username1", "John", "Smith"},
|
| - // Whether attribute values are upper or mixed case, it should not matter.
|
| + // It should not matter if attribute values are upper or mixed case.
|
| {{"USERNAME", NULL, "uSeRNaMe"}, "username1", "John", "Smith"},
|
| {{"uSeRNaMe", NULL, "USERNAME"}, "username1", "John", "Smith"}};
|
|
|
| @@ -233,7 +238,7 @@ TEST_F(PasswordFormConversionUtilsTest,
|
| PasswordFormBuilder builder(kTestFormActionURL);
|
| builder.AddUsernameField("username1", "John", cases[i].autocomplete[0]);
|
| builder.AddUsernameField("username2", "William", cases[i].autocomplete[1]);
|
| - builder.AddPasswordField("password", "secret");
|
| + builder.AddPasswordField("password", "secret", NULL);
|
| builder.AddUsernameField("username3", "Smith", cases[i].autocomplete[2]);
|
| builder.AddSubmitButton();
|
| std::string html = builder.ProduceHTML();
|
| @@ -253,4 +258,129 @@ TEST_F(PasswordFormConversionUtilsTest,
|
| }
|
| }
|
|
|
| +TEST_F(PasswordFormConversionUtilsTest,
|
| + PasswordFieldsWithAutocompleteAttributes) {
|
| + // Each test case consists of a set of parameters to be plugged into the
|
| + // PasswordFormBuilder below, plus the corresponding expectations.
|
| + struct TestCase {
|
| + const char* autocomplete[3];
|
| + const char* expected_password_element;
|
| + const char* expected_password_value;
|
| + const char* expected_new_password_element;
|
| + const char* expected_new_password_value;
|
| + } cases[] = {
|
| + // When there are elements marked with autocomplete='current-password',
|
| + // but no elements with 'new-password', we should treat the first of the
|
| + // former kind as the current password, and ignore all other password
|
| + // fields, assuming they are not intentionally not marked. They might be
|
| + // for other purposes, such as PINs, OTPs, and the like. Actual values in
|
| + // the password fields should be ignored in all cases below.
|
| + {{"current-password", NULL, NULL}, "password1", "alpha", "", ""},
|
| + {{NULL, "current-password", NULL}, "password2", "beta", "", ""},
|
| + {{NULL, NULL, "current-password"}, "password3", "gamma", "", ""},
|
| + {{NULL, "current-password", "current-password"},
|
| + "password2", "beta", "", ""},
|
| + {{"current-password", NULL, "current-password"},
|
| + "password1", "alpha", "", ""},
|
| + {{"current-password", "current-password", NULL},
|
| + "password1", "alpha", "", ""},
|
| + {{"current-password", "current-password", "current-password"},
|
| + "password1", "alpha", "", ""},
|
| + // The same goes vice versa for autocomplete='new-password'.
|
| + {{"new-password", NULL, NULL}, "", "", "password1", "alpha"},
|
| + {{NULL, "new-password", NULL}, "", "", "password2", "beta"},
|
| + {{NULL, NULL, "new-password"}, "", "", "password3", "gamma"},
|
| + {{NULL, "new-password", "new-password"}, "", "", "password2", "beta"},
|
| + {{"new-password", NULL, "new-password"}, "", "", "password1", "alpha"},
|
| + {{"new-password", "new-password", NULL}, "", "", "password1", "alpha"},
|
| + {{"new-password", "new-password", "new-password"},
|
| + "", "", "password1", "alpha"},
|
| + // When there is one element marked with autocomplete='current-password',
|
| + // and one with 'new-password', just comply, regardless of their order.
|
| + // Ignore the unmarked password field(s) for the same reason as above.
|
| + {{"current-password", "new-password", NULL},
|
| + "password1", "alpha", "password2", "beta"},
|
| + {{"current-password", NULL, "new-password"},
|
| + "password1", "alpha", "password3", "gamma"},
|
| + {{NULL, "current-password", "new-password"},
|
| + "password2", "beta", "password3", "gamma"},
|
| + {{"new-password", "current-password", NULL},
|
| + "password2", "beta", "password1", "alpha"},
|
| + {{"new-password", NULL, "current-password"},
|
| + "password3", "gamma", "password1", "alpha"},
|
| + {{NULL, "new-password", "current-password"},
|
| + "password3", "gamma", "password2", "beta"},
|
| + // In case of duplicated elements of either kind, go with the first one of
|
| + // its kind.
|
| + {{"current-password", "current-password", "new-password"},
|
| + "password1", "alpha", "password3", "gamma"},
|
| + {{"current-password", "new-password", "current-password"},
|
| + "password1", "alpha", "password2", "beta"},
|
| + {{"new-password", "current-password", "current-password"},
|
| + "password2", "beta", "password1", "alpha"},
|
| + {{"current-password", "new-password", "new-password"},
|
| + "password1", "alpha", "password2", "beta"},
|
| + {{"new-password", "current-password", "new-password"},
|
| + "password2", "beta", "password1", "alpha"},
|
| + {{"new-password", "new-password", "current-password"},
|
| + "password3", "gamma", "password1", "alpha"},
|
| + // When there is an empty autocomplete attribute (i.e. autocomplete=""),
|
| + // it should have the same effect as having no attribute whatsoever.
|
| + {{"current-password", "", ""},
|
| + "password1", "alpha", "", ""},
|
| + {{"", "", "new-password"},
|
| + "", "", "password3", "gamma"},
|
| + {{"", "new-password", ""},
|
| + "", "", "password2", "beta"},
|
| + {{"", "current-password", "current-password"},
|
| + "password2", "beta", "", ""},
|
| + {{"new-password", "", "new-password"}, "", "", "password1", "alpha"},
|
| + {{"new-password", "", "current-password"},
|
| + "password3", "gamma", "password1", "alpha"},
|
| + // It should not matter if attribute values are upper or mixed case.
|
| + {{NULL, "current-password", NULL},
|
| + "password2", "beta", "", ""},
|
| + {{NULL, "CURRENT-PASSWORD", NULL},
|
| + "password2", "beta", "", ""},
|
| + {{NULL, "new-password", NULL},
|
| + "", "", "password2", "beta"},
|
| + {{NULL, "nEw-PaSsWoRd", NULL},
|
| + "", "", "password2", "beta"}};
|
| +
|
| + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
|
| + SCOPED_TRACE(testing::Message() << "Iteration " << i);
|
| +
|
| + PasswordFormBuilder builder(kTestFormActionURL);
|
| + builder.AddPasswordField("password1", "alpha", cases[i].autocomplete[0]);
|
| + builder.AddUsernameField("username1", "William", NULL);
|
| + builder.AddPasswordField("password2", "beta", cases[i].autocomplete[1]);
|
| + builder.AddUsernameField("username2", "Smith", NULL);
|
| + builder.AddPasswordField("password3", "gamma", cases[i].autocomplete[2]);
|
| + builder.AddSubmitButton();
|
| + std::string html = builder.ProduceHTML();
|
| +
|
| + scoped_ptr<PasswordForm> password_form;
|
| + ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form));
|
| + ASSERT_NE(static_cast<PasswordForm*>(NULL), password_form.get());
|
| +
|
| + // Any constellation of password autocomplete attributes should have no
|
| + // effect on that the first text-type input field before a password field
|
| + // should be selected as the username.
|
| + // TODO(engedy): Double-check whether this is the intended behavior.
|
| + EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
|
| + EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
|
| + EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element),
|
| + password_form->password_element);
|
| + EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value),
|
| + password_form->password_value);
|
| + EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element),
|
| + password_form->new_password_element);
|
| + EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value),
|
| + password_form->new_password_value);
|
| + ASSERT_EQ(1u, password_form->other_possible_usernames.size());
|
| + EXPECT_EQ(base::UTF8ToUTF16("Smith"),
|
| + password_form->other_possible_usernames[0]);
|
| + }
|
| +}
|
| +
|
| } // namespace autofill
|
|
|