| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/strings/string16.h" | 5 #include "base/strings/string16.h" |
| 6 #include "base/strings/string_util.h" | 6 #include "base/strings/string_util.h" |
| 7 #include "base/strings/stringprintf.h" | 7 #include "base/strings/stringprintf.h" |
| 8 #include "base/strings/utf_string_conversions.h" | 8 #include "base/strings/utf_string_conversions.h" |
| 9 #include "components/autofill/content/renderer/password_form_conversion_utils.h" | 9 #include "components/autofill/content/renderer/password_form_conversion_utils.h" |
| 10 #include "components/autofill/core/common/password_form.h" | 10 #include "components/autofill/core/common/password_form.h" |
| 11 #include "content/public/test/render_view_test.h" | 11 #include "content/public/test/render_view_test.h" |
| 12 #include "testing/gmock/include/gmock/gmock.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" | 13 #include "testing/gtest/include/gtest/gtest.h" |
| 13 #include "third_party/WebKit/public/platform/WebVector.h" | 14 #include "third_party/WebKit/public/platform/WebVector.h" |
| 14 #include "third_party/WebKit/public/web/WebDocument.h" | 15 #include "third_party/WebKit/public/web/WebDocument.h" |
| 16 #include "third_party/WebKit/public/web/WebFormControlElement.h" |
| 15 #include "third_party/WebKit/public/web/WebFormElement.h" | 17 #include "third_party/WebKit/public/web/WebFormElement.h" |
| 18 #include "third_party/WebKit/public/web/WebInputElement.h" |
| 16 #include "third_party/WebKit/public/web/WebLocalFrame.h" | 19 #include "third_party/WebKit/public/web/WebLocalFrame.h" |
| 17 | 20 |
| 21 using blink::WebFormControlElement; |
| 18 using blink::WebFormElement; | 22 using blink::WebFormElement; |
| 19 using blink::WebFrame; | 23 using blink::WebFrame; |
| 24 using blink::WebInputElement; |
| 20 using blink::WebVector; | 25 using blink::WebVector; |
| 21 | 26 |
| 22 namespace autofill { | 27 namespace autofill { |
| 23 namespace { | 28 namespace { |
| 24 | 29 |
| 25 const char kTestFormActionURL[] = "http://cnn.com"; | 30 const char kTestFormActionURL[] = "http://cnn.com"; |
| 26 | 31 |
| 27 // A builder to produce HTML code for a password form composed of the desired | 32 // A builder to produce HTML code for a password form composed of the desired |
| 28 // number and kinds of username and password fields. | 33 // number and kinds of username and password fields. |
| 29 class PasswordFormBuilder { | 34 class PasswordFormBuilder { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 58 const char* value, | 63 const char* value, |
| 59 const char* autocomplete) { | 64 const char* autocomplete) { |
| 60 std::string autocomplete_attribute(autocomplete ? | 65 std::string autocomplete_attribute(autocomplete ? |
| 61 base::StringPrintf("autocomplete=\"%s\"", autocomplete): ""); | 66 base::StringPrintf("autocomplete=\"%s\"", autocomplete): ""); |
| 62 base::StringAppendF( | 67 base::StringAppendF( |
| 63 &html_, | 68 &html_, |
| 64 "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>", | 69 "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>", |
| 65 name_and_id, name_and_id, value, autocomplete_attribute.c_str()); | 70 name_and_id, name_and_id, value, autocomplete_attribute.c_str()); |
| 66 } | 71 } |
| 67 | 72 |
| 68 // Appends a new submit-type field at the end of the form. | 73 // Appends a disabled text-type field at the end of the form. |
| 69 void AddSubmitButton() { | 74 void AddDisabledUsernameField() { |
| 70 html_ += "<INPUT type=\"submit\" name=\"submit\" value=\"Submit\"/>"; | 75 html_ += "<INPUT type=\"text\" disabled/>"; |
| 76 } |
| 77 |
| 78 // Appends a disabled password-type field at the end of the form. |
| 79 void AddDisabledPasswordField() { |
| 80 html_ += "<INPUT type=\"password\" disabled/>"; |
| 81 } |
| 82 |
| 83 // Appends a new submit-type field at the end of the form with the specified |
| 84 // |name|. If |activated| is true, the test will emulate as if this button |
| 85 // were used to submit the form. |
| 86 void AddSubmitButton(const char* name, bool activated) { |
| 87 base::StringAppendF( |
| 88 &html_, |
| 89 "<INPUT type=\"submit\" name=\"%s\" value=\"Submit\" %s/>", |
| 90 name, activated ? "set-activated-submit" : ""); |
| 71 } | 91 } |
| 72 | 92 |
| 73 // Returns the HTML code for the form containing the fields that have been | 93 // Returns the HTML code for the form containing the fields that have been |
| 74 // added so far. | 94 // added so far. |
| 75 std::string ProduceHTML() const { | 95 std::string ProduceHTML() const { |
| 76 return html_ + "</FORM>"; | 96 return html_ + "</FORM>"; |
| 77 } | 97 } |
| 78 | 98 |
| 79 private: | 99 private: |
| 80 std::string html_; | 100 std::string html_; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 96 scoped_ptr<PasswordForm>* password_form) { | 116 scoped_ptr<PasswordForm>* password_form) { |
| 97 LoadHTML(html.c_str()); | 117 LoadHTML(html.c_str()); |
| 98 | 118 |
| 99 WebFrame* frame = GetMainFrame(); | 119 WebFrame* frame = GetMainFrame(); |
| 100 ASSERT_NE(static_cast<WebFrame*>(NULL), frame); | 120 ASSERT_NE(static_cast<WebFrame*>(NULL), frame); |
| 101 | 121 |
| 102 WebVector<WebFormElement> forms; | 122 WebVector<WebFormElement> forms; |
| 103 frame->document().forms(forms); | 123 frame->document().forms(forms); |
| 104 ASSERT_EQ(1U, forms.size()); | 124 ASSERT_EQ(1U, forms.size()); |
| 105 | 125 |
| 126 WebVector<WebFormControlElement> control_elements; |
| 127 forms[0].getFormControlElements(control_elements); |
| 128 for (size_t i = 0; i < control_elements.size(); ++i) { |
| 129 WebInputElement* input_element = toWebInputElement(&control_elements[i]); |
| 130 if (input_element->hasAttribute("set-activated-submit")) |
| 131 input_element->setActivatedSubmit(true); |
| 132 } |
| 133 |
| 106 *password_form = CreatePasswordForm(forms[0]); | 134 *password_form = CreatePasswordForm(forms[0]); |
| 107 } | 135 } |
| 108 | 136 |
| 109 private: | 137 private: |
| 110 DISALLOW_COPY_AND_ASSIGN(PasswordFormConversionUtilsTest); | 138 DISALLOW_COPY_AND_ASSIGN(PasswordFormConversionUtilsTest); |
| 111 }; | 139 }; |
| 112 | 140 |
| 113 } // namespace | 141 } // namespace |
| 114 | 142 |
| 115 TEST_F(PasswordFormConversionUtilsTest, ValidWebFormElementToPasswordForm) { | 143 TEST_F(PasswordFormConversionUtilsTest, BasicFormAttributes) { |
| 116 PasswordFormBuilder builder(kTestFormActionURL); | 144 PasswordFormBuilder builder(kTestFormActionURL); |
| 117 builder.AddUsernameField("username", "johnsmith", NULL); | 145 builder.AddUsernameField("username", "johnsmith", NULL); |
| 118 builder.AddSubmitButton(); | 146 builder.AddSubmitButton("inactive_submit", false); |
| 147 builder.AddSubmitButton("active_submit", true); |
| 148 builder.AddSubmitButton("inactive_submit2", false); |
| 119 builder.AddPasswordField("password", "secret", NULL); | 149 builder.AddPasswordField("password", "secret", NULL); |
| 120 std::string html = builder.ProduceHTML(); | 150 std::string html = builder.ProduceHTML(); |
| 121 | 151 |
| 122 scoped_ptr<PasswordForm> password_form; | 152 scoped_ptr<PasswordForm> password_form; |
| 123 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); | 153 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 124 ASSERT_NE(static_cast<PasswordForm*>(NULL), password_form.get()); | 154 ASSERT_TRUE(password_form); |
| 125 | 155 |
| 126 EXPECT_EQ("data:", password_form->signon_realm); | 156 EXPECT_EQ("data:", password_form->signon_realm); |
| 127 EXPECT_EQ(GURL(kTestFormActionURL), password_form->action); | 157 EXPECT_EQ(GURL(kTestFormActionURL), password_form->action); |
| 158 EXPECT_EQ(base::UTF8ToUTF16("active_submit"), password_form->submit_element); |
| 128 EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); | 159 EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); |
| 129 EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); | 160 EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); |
| 130 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); | 161 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); |
| 131 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); | 162 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); |
| 132 EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form->scheme); | 163 EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form->scheme); |
| 133 EXPECT_FALSE(password_form->ssl_valid); | 164 EXPECT_FALSE(password_form->ssl_valid); |
| 134 EXPECT_FALSE(password_form->preferred); | 165 EXPECT_FALSE(password_form->preferred); |
| 135 EXPECT_FALSE(password_form->blacklisted_by_user); | 166 EXPECT_FALSE(password_form->blacklisted_by_user); |
| 136 EXPECT_EQ(PasswordForm::TYPE_MANUAL, password_form->type); | 167 EXPECT_EQ(PasswordForm::TYPE_MANUAL, password_form->type); |
| 168 EXPECT_FALSE(password_form->use_additional_authentication); |
| 137 } | 169 } |
| 138 | 170 |
| 139 TEST_F(PasswordFormConversionUtilsTest, InvalidWebFormElementToPasswordForm) { | 171 TEST_F(PasswordFormConversionUtilsTest, DisabledFieldsAreIgnored) { |
| 140 PasswordFormBuilder builder("invalid_target"); | 172 PasswordFormBuilder builder(kTestFormActionURL); |
| 141 builder.AddUsernameField("username", "johnsmith", NULL); | 173 builder.AddUsernameField("username", "johnsmith", NULL); |
| 142 builder.AddSubmitButton(); | 174 builder.AddDisabledUsernameField(); |
| 175 builder.AddDisabledPasswordField(); |
| 143 builder.AddPasswordField("password", "secret", NULL); | 176 builder.AddPasswordField("password", "secret", NULL); |
| 177 builder.AddSubmitButton("submit", true); |
| 144 std::string html = builder.ProduceHTML(); | 178 std::string html = builder.ProduceHTML(); |
| 145 | 179 |
| 146 scoped_ptr<PasswordForm> password_form; | 180 scoped_ptr<PasswordForm> password_form; |
| 147 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); | 181 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 148 ASSERT_EQ(static_cast<PasswordForm*>(NULL), password_form.get()); | 182 ASSERT_TRUE(password_form); |
| 183 EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); |
| 184 EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); |
| 185 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); |
| 186 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); |
| 149 } | 187 } |
| 150 | 188 |
| 151 TEST_F(PasswordFormConversionUtilsTest, | 189 TEST_F(PasswordFormConversionUtilsTest, IdentifyingUsernameFields) { |
| 152 WebFormWithMultipleUseNameAndPassWordFieldsToPasswordForm) { | |
| 153 PasswordFormBuilder builder(kTestFormActionURL); | |
| 154 builder.AddUsernameField("username1", "John", NULL); | |
| 155 builder.AddPasswordField("password1", "oldsecret", NULL); | |
| 156 builder.AddUsernameField("username2", "William", NULL); | |
| 157 builder.AddPasswordField("password2", "secret", NULL); | |
| 158 builder.AddUsernameField("username3", "Smith", NULL); | |
| 159 builder.AddPasswordField("password3", "secret", NULL); | |
| 160 builder.AddSubmitButton(); | |
| 161 std::string html = builder.ProduceHTML(); | |
| 162 | |
| 163 scoped_ptr<PasswordForm> password_form; | |
| 164 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); | |
| 165 ASSERT_NE(static_cast<PasswordForm*>(NULL), password_form.get()); | |
| 166 | |
| 167 EXPECT_EQ("data:", password_form->signon_realm); | |
| 168 EXPECT_EQ(GURL(kTestFormActionURL), password_form->action); | |
| 169 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element); | |
| 170 EXPECT_EQ(base::UTF8ToUTF16("John"), password_form->username_value); | |
| 171 EXPECT_EQ(base::UTF8ToUTF16("password1"), | |
| 172 password_form->password_element); | |
| 173 EXPECT_EQ(base::UTF8ToUTF16("oldsecret"), password_form->password_value); | |
| 174 EXPECT_EQ(base::UTF8ToUTF16("password2"), | |
| 175 password_form->new_password_element); | |
| 176 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->new_password_value); | |
| 177 ASSERT_EQ(2u, password_form->other_possible_usernames.size()); | |
| 178 EXPECT_EQ(base::UTF8ToUTF16("William"), | |
| 179 password_form->other_possible_usernames[0]); | |
| 180 EXPECT_EQ(base::UTF8ToUTF16("Smith"), | |
| 181 password_form->other_possible_usernames[1]); | |
| 182 EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form->scheme); | |
| 183 EXPECT_FALSE(password_form->ssl_valid); | |
| 184 EXPECT_FALSE(password_form->preferred); | |
| 185 EXPECT_FALSE(password_form->blacklisted_by_user); | |
| 186 EXPECT_EQ(PasswordForm::TYPE_MANUAL, password_form->type); | |
| 187 } | |
| 188 | |
| 189 TEST_F(PasswordFormConversionUtilsTest, | |
| 190 WebFormwithThreeDifferentPasswordsToPasswordForm) { | |
| 191 PasswordFormBuilder builder(kTestFormActionURL); | |
| 192 builder.AddUsernameField("username1", "John", NULL); | |
| 193 builder.AddPasswordField("password1", "alpha", NULL); | |
| 194 builder.AddPasswordField("password2", "beta", NULL); | |
| 195 builder.AddPasswordField("password3", "gamma", NULL); | |
| 196 builder.AddSubmitButton(); | |
| 197 std::string html = builder.ProduceHTML(); | |
| 198 | |
| 199 scoped_ptr<PasswordForm> password_form; | |
| 200 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); | |
| 201 ASSERT_EQ(static_cast<PasswordForm*>(NULL), password_form.get()); | |
| 202 } | |
| 203 | |
| 204 TEST_F(PasswordFormConversionUtilsTest, | |
| 205 UsernameFieldsWithAutocompleteAttributes) { | |
| 206 // Each test case consists of a set of parameters to be plugged into the | 190 // Each test case consists of a set of parameters to be plugged into the |
| 207 // PasswordFormBuilder below, plus the corresponding expectations. | 191 // PasswordFormBuilder below, plus the corresponding expectations. |
| 208 struct TestCase { | 192 struct TestCase { |
| 209 const char* autocomplete[3]; | 193 const char* autocomplete[3]; |
| 210 const char* expected_username_element; | 194 const char* expected_username_element; |
| 211 const char* expected_username_value; | 195 const char* expected_username_value; |
| 212 const char* expected_other_possible_usernames; | 196 const char* expected_other_possible_usernames; |
| 213 } cases[] = { | 197 } cases[] = { |
| 198 // When no elements are marked with autocomplete='username', the text-type |
| 199 // input field before the first password element should get selected as |
| 200 // the username, and the rest should be marked as alternatives. |
| 201 {{NULL, NULL, NULL}, "username2", "William", "John+Smith"}, |
| 214 // When a sole element is marked with autocomplete='username', it should | 202 // When a sole element is marked with autocomplete='username', it should |
| 215 // be treated as the username for sure, with no other_possible_usernames. | 203 // be treated as the username for sure, with no other_possible_usernames. |
| 216 {{"username", NULL, NULL}, "username1", "John", ""}, | 204 {{"username", NULL, NULL}, "username1", "John", ""}, |
| 217 {{NULL, "username", NULL}, "username2", "William", ""}, | 205 {{NULL, "username", NULL}, "username2", "William", ""}, |
| 218 {{NULL, NULL, "username"}, "username3", "Smith", ""}, | 206 {{NULL, NULL, "username"}, "username3", "Smith", ""}, |
| 219 // When >=2 elements have the attribute, the first should be selected as | 207 // When >=2 elements have the attribute, the first should be selected as |
| 220 // the username, and the rest should go to other_possible_usernames. | 208 // the username, and the rest should go to other_possible_usernames. |
| 221 {{"username", "username", NULL}, "username1", "John", "William"}, | 209 {{"username", "username", NULL}, "username1", "John", "William"}, |
| 222 {{NULL, "username", "username"}, "username2", "William", "Smith"}, | 210 {{NULL, "username", "username"}, "username2", "William", "Smith"}, |
| 223 {{"username", NULL, "username"}, "username1", "John", "Smith"}, | 211 {{"username", NULL, "username"}, "username1", "John", "Smith"}, |
| 224 {{"username", "username", "username"}, "username1", "John", | 212 {{"username", "username", "username"}, "username1", "John", |
| 225 "William+Smith"}, | 213 "William+Smith"}, |
| 226 // When there is an empty autocomplete attribute (i.e. autocomplete=""), | 214 // When there is an empty autocomplete attribute (i.e. autocomplete=""), |
| 227 // it should have the same effect as having no attribute whatsoever. | 215 // it should have the same effect as having no attribute whatsoever. |
| 228 {{"", "", ""}, "username2", "William", "John+Smith"}, | 216 {{"", "", ""}, "username2", "William", "John+Smith"}, |
| 229 {{"", "", "username"}, "username3", "Smith", ""}, | 217 {{"", "", "username"}, "username3", "Smith", ""}, |
| 230 {{"username", "", "username"}, "username1", "John", "Smith"}, | 218 {{"username", "", "username"}, "username1", "John", "Smith"}, |
| 231 // It should not matter if attribute values are upper or mixed case. | 219 // It should not matter if attribute values are upper or mixed case. |
| 232 {{"USERNAME", NULL, "uSeRNaMe"}, "username1", "John", "Smith"}, | 220 {{"USERNAME", NULL, "uSeRNaMe"}, "username1", "John", "Smith"}, |
| 233 {{"uSeRNaMe", NULL, "USERNAME"}, "username1", "John", "Smith"}}; | 221 {{"uSeRNaMe", NULL, "USERNAME"}, "username1", "John", "Smith"}}; |
| 234 | 222 |
| 235 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { | 223 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { |
| 224 for (size_t nonempty_username_fields = 0; nonempty_username_fields < 2; |
| 225 ++nonempty_username_fields) { |
| 226 SCOPED_TRACE(testing::Message() |
| 227 << "Iteration " << i << " " |
| 228 << (nonempty_username_fields ? "nonempty" : "empty")); |
| 229 |
| 230 // Repeat each test once with empty, and once with non-empty usernames. |
| 231 // In the former case, no empty other_possible_usernames should be saved. |
| 232 const char* names[3]; |
| 233 if (nonempty_username_fields) { |
| 234 names[0] = "John"; |
| 235 names[1] = "William"; |
| 236 names[2] = "Smith"; |
| 237 } else { |
| 238 names[0] = names[1] = names[2] = ""; |
| 239 } |
| 240 |
| 241 PasswordFormBuilder builder(kTestFormActionURL); |
| 242 builder.AddUsernameField("username1", names[0], cases[i].autocomplete[0]); |
| 243 builder.AddUsernameField("username2", names[1], cases[i].autocomplete[1]); |
| 244 builder.AddPasswordField("password", "secret", NULL); |
| 245 builder.AddUsernameField("username3", names[2], cases[i].autocomplete[2]); |
| 246 builder.AddPasswordField("password2", "othersecret", NULL); |
| 247 builder.AddSubmitButton("submit", true); |
| 248 std::string html = builder.ProduceHTML(); |
| 249 |
| 250 scoped_ptr<PasswordForm> password_form; |
| 251 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 252 ASSERT_TRUE(password_form); |
| 253 |
| 254 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element), |
| 255 password_form->username_element); |
| 256 |
| 257 if (nonempty_username_fields) { |
| 258 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value), |
| 259 password_form->username_value); |
| 260 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_other_possible_usernames), |
| 261 JoinString(password_form->other_possible_usernames, '+')); |
| 262 } else { |
| 263 EXPECT_TRUE(password_form->username_value.empty()); |
| 264 EXPECT_TRUE(password_form->other_possible_usernames.empty()); |
| 265 } |
| 266 |
| 267 // Do a basic sanity check that we are still having a password field. |
| 268 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); |
| 269 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); |
| 270 } |
| 271 } |
| 272 } |
| 273 |
| 274 TEST_F(PasswordFormConversionUtilsTest, IdentifyingTwoPasswordFields) { |
| 275 // Each test case consists of a set of parameters to be plugged into the |
| 276 // PasswordFormBuilder below, plus the corresponding expectations. |
| 277 struct TestCase { |
| 278 const char* password_values[2]; |
| 279 const char* expected_password_element; |
| 280 const char* expected_password_value; |
| 281 const char* expected_new_password_element; |
| 282 const char* expected_new_password_value; |
| 283 } cases[] = { |
| 284 // Twp non-empty fields with the same value should be treated as a new |
| 285 // password field plus a confirmation field for the new password. |
| 286 {{"alpha", "alpha"}, "", "", "password1", "alpha"}, |
| 287 // The same goes if the fields are yet empty: we speculate that we will |
| 288 // identify them as new password fields once they are filled out, and we |
| 289 // want to keep our abstract interpretation of the form less flaky. |
| 290 {{"", ""}, "", "", "password1", ""}, |
| 291 // Two different values should be treated as a password change form, one |
| 292 // that also asks for the current password, but only once for the new. |
| 293 {{"alpha", ""}, "password1", "alpha", "password2", ""}, |
| 294 {{"", "beta"}, "password1", "", "password2", "beta"}, |
| 295 {{"alpha", "beta"}, "password1", "alpha", "password2", "beta"}}; |
| 296 |
| 297 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { |
| 236 SCOPED_TRACE(testing::Message() << "Iteration " << i); | 298 SCOPED_TRACE(testing::Message() << "Iteration " << i); |
| 237 | 299 |
| 238 PasswordFormBuilder builder(kTestFormActionURL); | 300 PasswordFormBuilder builder(kTestFormActionURL); |
| 239 builder.AddUsernameField("username1", "John", cases[i].autocomplete[0]); | 301 builder.AddPasswordField("password1", cases[i].password_values[0], NULL); |
| 240 builder.AddUsernameField("username2", "William", cases[i].autocomplete[1]); | 302 builder.AddUsernameField("username1", "William", NULL); |
| 241 builder.AddPasswordField("password", "secret", NULL); | 303 builder.AddPasswordField("password2", cases[i].password_values[1], NULL); |
| 242 builder.AddUsernameField("username3", "Smith", cases[i].autocomplete[2]); | 304 builder.AddUsernameField("username2", "Smith", NULL); |
| 243 builder.AddSubmitButton(); | 305 builder.AddSubmitButton("submit", true); |
| 244 std::string html = builder.ProduceHTML(); | 306 std::string html = builder.ProduceHTML(); |
| 245 | 307 |
| 246 scoped_ptr<PasswordForm> password_form; | 308 scoped_ptr<PasswordForm> password_form; |
| 247 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); | 309 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 248 ASSERT_NE(static_cast<PasswordForm*>(NULL), password_form.get()); | 310 ASSERT_TRUE(password_form); |
| 249 | 311 |
| 250 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element), | 312 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element), |
| 251 password_form->username_element); | 313 password_form->password_element); |
| 252 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value), | 314 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value), |
| 253 password_form->username_value); | 315 password_form->password_value); |
| 254 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); | 316 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element), |
| 255 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); | 317 password_form->new_password_element); |
| 256 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_other_possible_usernames), | 318 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value), |
| 257 JoinString(password_form->other_possible_usernames, '+')); | 319 password_form->new_password_value); |
| 320 |
| 321 // Do a basic sanity check that we are still selecting the right username. |
| 322 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element); |
| 323 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); |
| 324 EXPECT_THAT(password_form->other_possible_usernames, |
| 325 testing::ElementsAre(base::UTF8ToUTF16("Smith"))); |
| 326 } |
| 327 } |
| 328 |
| 329 TEST_F(PasswordFormConversionUtilsTest, IdentifyingThreePasswordFields) { |
| 330 // Each test case consists of a set of parameters to be plugged into the |
| 331 // PasswordFormBuilder below, plus the corresponding expectations. |
| 332 struct TestCase { |
| 333 const char* password_values[3]; |
| 334 const char* expected_password_element; |
| 335 const char* expected_password_value; |
| 336 const char* expected_new_password_element; |
| 337 const char* expected_new_password_value; |
| 338 } cases[] = { |
| 339 // Two fields with the same value, and one different: we should treat this |
| 340 // as a password change form with confirmation for the new password. Note |
| 341 // that we only recognize (current + new + new) and (new + new + current) |
| 342 // without autocomplete attributes. |
| 343 {{"alpha", "", ""}, "password1", "alpha", "password2", ""}, |
| 344 {{"", "beta", "beta"}, "password1", "", "password2", "beta"}, |
| 345 {{"alpha", "beta", "beta"}, "password1", "alpha", "password2", "beta"}, |
| 346 {{"beta", "beta", "alpha"}, "password3", "alpha", "password1", "beta"}, |
| 347 // If the fields are yet empty, we speculate that we will identify them as |
| 348 // (current + new + new) once they are filled out, so we should classify |
| 349 // them the same for now to keep our abstract interpretation less flaky. |
| 350 {{"", "", ""}, "password1", "", "password2", ""}}; |
| 351 // Note: In all other cases, we give up and consider the form invalid. |
| 352 // This is tested in InvalidFormDueToConfusingPasswordFields. |
| 353 |
| 354 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { |
| 355 SCOPED_TRACE(testing::Message() << "Iteration " << i); |
| 356 |
| 357 PasswordFormBuilder builder(kTestFormActionURL); |
| 358 builder.AddPasswordField("password1", cases[i].password_values[0], NULL); |
| 359 builder.AddUsernameField("username1", "William", NULL); |
| 360 builder.AddPasswordField("password2", cases[i].password_values[1], NULL); |
| 361 builder.AddUsernameField("username2", "Smith", NULL); |
| 362 builder.AddPasswordField("password3", cases[i].password_values[2], NULL); |
| 363 builder.AddSubmitButton("submit", true); |
| 364 std::string html = builder.ProduceHTML(); |
| 365 |
| 366 scoped_ptr<PasswordForm> password_form; |
| 367 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 368 ASSERT_TRUE(password_form); |
| 369 |
| 370 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element), |
| 371 password_form->password_element); |
| 372 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value), |
| 373 password_form->password_value); |
| 374 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element), |
| 375 password_form->new_password_element); |
| 376 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value), |
| 377 password_form->new_password_value); |
| 378 |
| 379 // Do a basic sanity check that we are still selecting the right username. |
| 380 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element); |
| 381 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); |
| 382 EXPECT_THAT(password_form->other_possible_usernames, |
| 383 testing::ElementsAre(base::UTF8ToUTF16("Smith"))); |
| 258 } | 384 } |
| 259 } | 385 } |
| 260 | 386 |
| 261 TEST_F(PasswordFormConversionUtilsTest, | 387 TEST_F(PasswordFormConversionUtilsTest, |
| 262 PasswordFieldsWithAutocompleteAttributes) { | 388 IdentifyingPasswordFieldsWithAutocompleteAttributes) { |
| 263 // Each test case consists of a set of parameters to be plugged into the | 389 // Each test case consists of a set of parameters to be plugged into the |
| 264 // PasswordFormBuilder below, plus the corresponding expectations. | 390 // PasswordFormBuilder below, plus the corresponding expectations. |
| 265 struct TestCase { | 391 struct TestCase { |
| 266 const char* autocomplete[3]; | 392 const char* autocomplete[3]; |
| 267 const char* expected_password_element; | 393 const char* expected_password_element; |
| 268 const char* expected_password_value; | 394 const char* expected_password_value; |
| 269 const char* expected_new_password_element; | 395 const char* expected_new_password_element; |
| 270 const char* expected_new_password_value; | 396 const char* expected_new_password_value; |
| 271 } cases[] = { | 397 } cases[] = { |
| 272 // When there are elements marked with autocomplete='current-password', | 398 // When there are elements marked with autocomplete='current-password', |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 354 "password2", "beta", "", ""}, | 480 "password2", "beta", "", ""}, |
| 355 {{NULL, "new-password", NULL}, | 481 {{NULL, "new-password", NULL}, |
| 356 "", "", "password2", "beta"}, | 482 "", "", "password2", "beta"}, |
| 357 {{NULL, "nEw-PaSsWoRd", NULL}, | 483 {{NULL, "nEw-PaSsWoRd", NULL}, |
| 358 "", "", "password2", "beta"}}; | 484 "", "", "password2", "beta"}}; |
| 359 | 485 |
| 360 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { | 486 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { |
| 361 SCOPED_TRACE(testing::Message() << "Iteration " << i); | 487 SCOPED_TRACE(testing::Message() << "Iteration " << i); |
| 362 | 488 |
| 363 PasswordFormBuilder builder(kTestFormActionURL); | 489 PasswordFormBuilder builder(kTestFormActionURL); |
| 490 builder.AddPasswordField("pin1", "123456", NULL); |
| 491 builder.AddPasswordField("pin2", "789101", NULL); |
| 364 builder.AddPasswordField("password1", "alpha", cases[i].autocomplete[0]); | 492 builder.AddPasswordField("password1", "alpha", cases[i].autocomplete[0]); |
| 365 builder.AddUsernameField("username1", "William", NULL); | 493 builder.AddUsernameField("username1", "William", NULL); |
| 366 builder.AddPasswordField("password2", "beta", cases[i].autocomplete[1]); | 494 builder.AddPasswordField("password2", "beta", cases[i].autocomplete[1]); |
| 367 builder.AddUsernameField("username2", "Smith", NULL); | 495 builder.AddUsernameField("username2", "Smith", NULL); |
| 368 builder.AddPasswordField("password3", "gamma", cases[i].autocomplete[2]); | 496 builder.AddPasswordField("password3", "gamma", cases[i].autocomplete[2]); |
| 369 builder.AddSubmitButton(); | 497 builder.AddSubmitButton("submit", true); |
| 370 std::string html = builder.ProduceHTML(); | 498 std::string html = builder.ProduceHTML(); |
| 371 | 499 |
| 372 scoped_ptr<PasswordForm> password_form; | 500 scoped_ptr<PasswordForm> password_form; |
| 373 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); | 501 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 374 ASSERT_NE(static_cast<PasswordForm*>(NULL), password_form.get()); | 502 ASSERT_TRUE(password_form); |
| 375 | 503 |
| 376 // Any constellation of password autocomplete attributes should have no | 504 // In the absence of username autocomplete attributes, the username should |
| 377 // effect on that the first text-type input field before a password field | 505 // be the text input field before the first password element. |
| 378 // should be selected as the username. | 506 // No constellation of password autocomplete attributes should change that. |
| 379 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element); | 507 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element); |
| 380 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); | 508 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); |
| 509 EXPECT_THAT(password_form->other_possible_usernames, |
| 510 testing::ElementsAre(base::UTF8ToUTF16("Smith"))); |
| 381 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element), | 511 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element), |
| 382 password_form->password_element); | 512 password_form->password_element); |
| 383 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value), | 513 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value), |
| 384 password_form->password_value); | 514 password_form->password_value); |
| 385 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element), | 515 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element), |
| 386 password_form->new_password_element); | 516 password_form->new_password_element); |
| 387 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value), | 517 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value), |
| 388 password_form->new_password_value); | 518 password_form->new_password_value); |
| 389 ASSERT_EQ(1u, password_form->other_possible_usernames.size()); | |
| 390 EXPECT_EQ(base::UTF8ToUTF16("Smith"), | |
| 391 password_form->other_possible_usernames[0]); | |
| 392 } | 519 } |
| 393 } | 520 } |
| 394 | 521 |
| 522 TEST_F(PasswordFormConversionUtilsTest, InvalidFormDueToBadActionURL) { |
| 523 PasswordFormBuilder builder("invalid_target"); |
| 524 builder.AddUsernameField("username", "JohnSmith", NULL); |
| 525 builder.AddSubmitButton("submit", true); |
| 526 builder.AddPasswordField("password", "secret", NULL); |
| 527 std::string html = builder.ProduceHTML(); |
| 528 |
| 529 scoped_ptr<PasswordForm> password_form; |
| 530 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 531 EXPECT_FALSE(password_form); |
| 532 } |
| 533 |
| 534 TEST_F(PasswordFormConversionUtilsTest, InvalidFormDueToNoPasswordFields) { |
| 535 PasswordFormBuilder builder(kTestFormActionURL); |
| 536 builder.AddUsernameField("username1", "John", NULL); |
| 537 builder.AddUsernameField("username2", "Smith", NULL); |
| 538 builder.AddSubmitButton("submit", true); |
| 539 std::string html = builder.ProduceHTML(); |
| 540 |
| 541 scoped_ptr<PasswordForm> password_form; |
| 542 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 543 EXPECT_FALSE(password_form); |
| 544 } |
| 545 |
| 546 TEST_F(PasswordFormConversionUtilsTest, |
| 547 InvalidFormsDueToConfusingPasswordFields) { |
| 548 // Each test case consists of a set of parameters to be plugged into the |
| 549 // PasswordFormBuilder below. |
| 550 const char* cases[][3] = { |
| 551 // No autocomplete attributes to guide us, and we see: |
| 552 // * three password values that are all different, |
| 553 // * three password values that are all the same; |
| 554 // * three password values with the first and last matching. |
| 555 // In any case, we should just give up on this form. |
| 556 {"alpha", "beta", "gamma"}, |
| 557 {"alpha", "alpha", "alpha"}, |
| 558 {"alpha", "beta", "alpha"}}; |
| 559 |
| 560 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { |
| 561 SCOPED_TRACE(testing::Message() << "Iteration " << i); |
| 562 |
| 563 PasswordFormBuilder builder(kTestFormActionURL); |
| 564 builder.AddUsernameField("username1", "John", NULL); |
| 565 builder.AddPasswordField("password1", cases[i][0], NULL); |
| 566 builder.AddPasswordField("password2", cases[i][1], NULL); |
| 567 builder.AddPasswordField("password3", cases[i][2], NULL); |
| 568 builder.AddSubmitButton("submit", true); |
| 569 std::string html = builder.ProduceHTML(); |
| 570 |
| 571 scoped_ptr<PasswordForm> password_form; |
| 572 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 573 EXPECT_FALSE(password_form); |
| 574 } |
| 575 } |
| 576 |
| 577 TEST_F(PasswordFormConversionUtilsTest, |
| 578 InvalidFormDueToTooManyPasswordFieldsWithoutAutocompleteAttributes) { |
| 579 PasswordFormBuilder builder(kTestFormActionURL); |
| 580 builder.AddUsernameField("username1", "John", NULL); |
| 581 builder.AddPasswordField("password1", "alpha", NULL); |
| 582 builder.AddPasswordField("password2", "alpha", NULL); |
| 583 builder.AddPasswordField("password3", "alpha", NULL); |
| 584 builder.AddPasswordField("password4", "alpha", NULL); |
| 585 builder.AddSubmitButton("submit", true); |
| 586 std::string html = builder.ProduceHTML(); |
| 587 |
| 588 scoped_ptr<PasswordForm> password_form; |
| 589 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 590 EXPECT_FALSE(password_form); |
| 591 } |
| 592 |
| 395 } // namespace autofill | 593 } // namespace autofill |
| OLD | NEW |