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 3// found in the LICENSE file. 4 5#include "base/strings/string16.h" 6#include "base/strings/string_util.h" 7#include "base/strings/stringprintf.h" 8#include "base/strings/utf_string_conversions.h" 9#include "components/autofill/content/renderer/password_form_conversion_utils.h" 10#include "components/autofill/core/common/password_form.h" 11#include "content/public/test/render_view_test.h" 12#include "testing/gmock/include/gmock/gmock.h" 13#include "testing/gtest/include/gtest/gtest.h" 14#include "third_party/WebKit/public/platform/WebVector.h" 15#include "third_party/WebKit/public/web/WebDocument.h" 16#include "third_party/WebKit/public/web/WebFormControlElement.h" 17#include "third_party/WebKit/public/web/WebFormElement.h" 18#include "third_party/WebKit/public/web/WebInputElement.h" 19#include "third_party/WebKit/public/web/WebLocalFrame.h" 20 21using blink::WebFormControlElement; 22using blink::WebFormElement; 23using blink::WebFrame; 24using blink::WebInputElement; 25using blink::WebVector; 26 27namespace autofill { 28namespace { 29 30const char kTestFormActionURL[] = "http://cnn.com"; 31 32// A builder to produce HTML code for a password form composed of the desired 33// number and kinds of username and password fields. 34class PasswordFormBuilder { 35 public: 36 // Creates a builder to start composing a new form. The form will have the 37 // specified |action| URL. 38 explicit PasswordFormBuilder(const char* action) { 39 base::StringAppendF( 40 &html_, "<FORM name=\"Test\" action=\"%s\" method=\"post\">", action); 41 } 42 43 // Appends a new text-type field at the end of the form, having the specified 44 // |name_and_id|, |value|, and |autocomplete| attributes. The |autocomplete| 45 // argument can take two special values, namely: 46 // 1.) NULL, causing no autocomplete attribute to be added, 47 // 2.) "", causing an empty attribute (i.e. autocomplete="") to be added. 48 void AddUsernameField(const char* name_and_id, 49 const char* value, 50 const char* autocomplete) { 51 std::string autocomplete_attribute(autocomplete ? 52 base::StringPrintf("autocomplete=\"%s\"", autocomplete) : ""); 53 base::StringAppendF( 54 &html_, 55 "<INPUT type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>", 56 name_and_id, name_and_id, value, autocomplete_attribute.c_str()); 57 } 58 59 // Appends a new password-type field at the end of the form, having the 60 // specified |name_and_id|, |value|, and |autocomplete| attributes. Special 61 // values for |autocomplete| are the same as in AddUsernameField. 62 void AddPasswordField(const char* name_and_id, 63 const char* value, 64 const char* autocomplete) { 65 std::string autocomplete_attribute(autocomplete ? 66 base::StringPrintf("autocomplete=\"%s\"", autocomplete): ""); 67 base::StringAppendF( 68 &html_, 69 "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>", 70 name_and_id, name_and_id, value, autocomplete_attribute.c_str()); 71 } 72 73 // Appends a disabled text-type field at the end of the form. 74 void AddDisabledUsernameField() { 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" : ""); 91 } 92 93 // Returns the HTML code for the form containing the fields that have been 94 // added so far. 95 std::string ProduceHTML() const { 96 return html_ + "</FORM>"; 97 } 98 99 private: 100 std::string html_; 101 102 DISALLOW_COPY_AND_ASSIGN(PasswordFormBuilder); 103}; 104 105class PasswordFormConversionUtilsTest : public content::RenderViewTest { 106 public: 107 PasswordFormConversionUtilsTest() : content::RenderViewTest() {} 108 virtual ~PasswordFormConversionUtilsTest() {} 109 110 protected: 111 // Loads the given |html|, retrieves the sole WebFormElement from it, and then 112 // calls CreatePasswordForm() to convert it into a |password_form|. Note that 113 // ASSERT() can only be used in void functions, this is why |password_form| is 114 // passed in as a pointer to a scoped_ptr. 115 void LoadHTMLAndConvertForm(const std::string& html, 116 scoped_ptr<PasswordForm>* password_form) { 117 LoadHTML(html.c_str()); 118 119 WebFrame* frame = GetMainFrame(); 120 ASSERT_NE(static_cast<WebFrame*>(NULL), frame); 121 122 WebVector<WebFormElement> forms; 123 frame->document().forms(forms); 124 ASSERT_EQ(1U, forms.size()); 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 134 *password_form = CreatePasswordForm(forms[0]); 135 } 136 137 private: 138 DISALLOW_COPY_AND_ASSIGN(PasswordFormConversionUtilsTest); 139}; 140 141} // namespace 142 143TEST_F(PasswordFormConversionUtilsTest, BasicFormAttributes) { 144 PasswordFormBuilder builder(kTestFormActionURL); 145 builder.AddUsernameField("username", "johnsmith", NULL); 146 builder.AddSubmitButton("inactive_submit", false); 147 builder.AddSubmitButton("active_submit", true); 148 builder.AddSubmitButton("inactive_submit2", false); 149 builder.AddPasswordField("password", "secret", NULL); 150 std::string html = builder.ProduceHTML(); 151 152 scoped_ptr<PasswordForm> password_form; 153 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); 154 ASSERT_TRUE(password_form); 155 156 EXPECT_EQ("data:", password_form->signon_realm); 157 EXPECT_EQ(GURL(kTestFormActionURL), password_form->action); 158 EXPECT_EQ(base::UTF8ToUTF16("active_submit"), password_form->submit_element); 159 EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); 160 EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); 161 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); 162 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); 163 EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form->scheme); 164 EXPECT_FALSE(password_form->ssl_valid); 165 EXPECT_FALSE(password_form->preferred); 166 EXPECT_FALSE(password_form->blacklisted_by_user); 167 EXPECT_EQ(PasswordForm::TYPE_MANUAL, password_form->type); 168 EXPECT_FALSE(password_form->use_additional_authentication); 169} 170 171TEST_F(PasswordFormConversionUtilsTest, DisabledFieldsAreIgnored) { 172 PasswordFormBuilder builder(kTestFormActionURL); 173 builder.AddUsernameField("username", "johnsmith", NULL); 174 builder.AddDisabledUsernameField(); 175 builder.AddDisabledPasswordField(); 176 builder.AddPasswordField("password", "secret", NULL); 177 builder.AddSubmitButton("submit", true); 178 std::string html = builder.ProduceHTML(); 179 180 scoped_ptr<PasswordForm> password_form; 181 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); 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); 187} 188 189TEST_F(PasswordFormConversionUtilsTest, IdentifyingUsernameFields) { 190 // Each test case consists of a set of parameters to be plugged into the 191 // PasswordFormBuilder below, plus the corresponding expectations. 192 struct TestCase { 193 const char* autocomplete[3]; 194 const char* expected_username_element; 195 const char* expected_username_value; 196 const char* expected_other_possible_usernames; 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"}, 202 // When a sole element is marked with autocomplete='username', it should 203 // be treated as the username for sure, with no other_possible_usernames. 204 {{"username", NULL, NULL}, "username1", "John", ""}, 205 {{NULL, "username", NULL}, "username2", "William", ""}, 206 {{NULL, NULL, "username"}, "username3", "Smith", ""}, 207 // When >=2 elements have the attribute, the first should be selected as 208 // the username, and the rest should go to other_possible_usernames. 209 {{"username", "username", NULL}, "username1", "John", "William"}, 210 {{NULL, "username", "username"}, "username2", "William", "Smith"}, 211 {{"username", NULL, "username"}, "username1", "John", "Smith"}, 212 {{"username", "username", "username"}, "username1", "John", 213 "William+Smith"}, 214 // When there is an empty autocomplete attribute (i.e. autocomplete=""), 215 // it should have the same effect as having no attribute whatsoever. 216 {{"", "", ""}, "username2", "William", "John+Smith"}, 217 {{"", "", "username"}, "username3", "Smith", ""}, 218 {{"username", "", "username"}, "username1", "John", "Smith"}, 219 // It should not matter if attribute values are upper or mixed case. 220 {{"USERNAME", NULL, "uSeRNaMe"}, "username1", "John", "Smith"}, 221 {{"uSeRNaMe", NULL, "USERNAME"}, "username1", "John", "Smith"}}; 222 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 274TEST_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) { 298 SCOPED_TRACE(testing::Message() << "Iteration " << i); 299 300 PasswordFormBuilder builder(kTestFormActionURL); 301 builder.AddPasswordField("password1", cases[i].password_values[0], NULL); 302 builder.AddUsernameField("username1", "William", NULL); 303 builder.AddPasswordField("password2", cases[i].password_values[1], NULL); 304 builder.AddUsernameField("username2", "Smith", NULL); 305 builder.AddSubmitButton("submit", true); 306 std::string html = builder.ProduceHTML(); 307 308 scoped_ptr<PasswordForm> password_form; 309 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); 310 ASSERT_TRUE(password_form); 311 312 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element), 313 password_form->password_element); 314 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value), 315 password_form->password_value); 316 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element), 317 password_form->new_password_element); 318 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value), 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 329TEST_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"))); 384 } 385} 386 387TEST_F(PasswordFormConversionUtilsTest, 388 IdentifyingPasswordFieldsWithAutocompleteAttributes) { 389 // Each test case consists of a set of parameters to be plugged into the 390 // PasswordFormBuilder below, plus the corresponding expectations. 391 struct TestCase { 392 const char* autocomplete[3]; 393 const char* expected_password_element; 394 const char* expected_password_value; 395 const char* expected_new_password_element; 396 const char* expected_new_password_value; 397 } cases[] = { 398 // When there are elements marked with autocomplete='current-password', 399 // but no elements with 'new-password', we should treat the first of the 400 // former kind as the current password, and ignore all other password 401 // fields, assuming they are not intentionally not marked. They might be 402 // for other purposes, such as PINs, OTPs, and the like. Actual values in 403 // the password fields should be ignored in all cases below. 404 {{"current-password", NULL, NULL}, 405 "password1", "alpha", "", ""}, 406 {{NULL, "current-password", NULL}, 407 "password2", "beta", "", ""}, 408 {{NULL, NULL, "current-password"}, 409 "password3", "gamma", "", ""}, 410 {{NULL, "current-password", "current-password"}, 411 "password2", "beta", "", ""}, 412 {{"current-password", NULL, "current-password"}, 413 "password1", "alpha", "", ""}, 414 {{"current-password", "current-password", NULL}, 415 "password1", "alpha", "", ""}, 416 {{"current-password", "current-password", "current-password"}, 417 "password1", "alpha", "", ""}, 418 // The same goes vice versa for autocomplete='new-password'. 419 {{"new-password", NULL, NULL}, 420 "", "", "password1", "alpha"}, 421 {{NULL, "new-password", NULL}, 422 "", "", "password2", "beta"}, 423 {{NULL, NULL, "new-password"}, 424 "", "", "password3", "gamma"}, 425 {{NULL, "new-password", "new-password"}, 426 "", "", "password2", "beta"}, 427 {{"new-password", NULL, "new-password"}, 428 "", "", "password1", "alpha"}, 429 {{"new-password", "new-password", NULL}, 430 "", "", "password1", "alpha"}, 431 {{"new-password", "new-password", "new-password"}, 432 "", "", "password1", "alpha"}, 433 // When there is one element marked with autocomplete='current-password', 434 // and one with 'new-password', just comply, regardless of their order. 435 // Ignore the unmarked password field(s) for the same reason as above. 436 {{"current-password", "new-password", NULL}, 437 "password1", "alpha", "password2", "beta"}, 438 {{"current-password", NULL, "new-password"}, 439 "password1", "alpha", "password3", "gamma"}, 440 {{NULL, "current-password", "new-password"}, 441 "password2", "beta", "password3", "gamma"}, 442 {{"new-password", "current-password", NULL}, 443 "password2", "beta", "password1", "alpha"}, 444 {{"new-password", NULL, "current-password"}, 445 "password3", "gamma", "password1", "alpha"}, 446 {{NULL, "new-password", "current-password"}, 447 "password3", "gamma", "password2", "beta"}, 448 // In case of duplicated elements of either kind, go with the first one of 449 // its kind. 450 {{"current-password", "current-password", "new-password"}, 451 "password1", "alpha", "password3", "gamma"}, 452 {{"current-password", "new-password", "current-password"}, 453 "password1", "alpha", "password2", "beta"}, 454 {{"new-password", "current-password", "current-password"}, 455 "password2", "beta", "password1", "alpha"}, 456 {{"current-password", "new-password", "new-password"}, 457 "password1", "alpha", "password2", "beta"}, 458 {{"new-password", "current-password", "new-password"}, 459 "password2", "beta", "password1", "alpha"}, 460 {{"new-password", "new-password", "current-password"}, 461 "password3", "gamma", "password1", "alpha"}, 462 // When there is an empty autocomplete attribute (i.e. autocomplete=""), 463 // it should have the same effect as having no attribute whatsoever. 464 {{"current-password", "", ""}, 465 "password1", "alpha", "", ""}, 466 {{"", "", "new-password"}, 467 "", "", "password3", "gamma"}, 468 {{"", "new-password", ""}, 469 "", "", "password2", "beta"}, 470 {{"", "current-password", "current-password"}, 471 "password2", "beta", "", ""}, 472 {{"new-password", "", "new-password"}, 473 "", "", "password1", "alpha"}, 474 {{"new-password", "", "current-password"}, 475 "password3", "gamma", "password1", "alpha"}, 476 // It should not matter if attribute values are upper or mixed case. 477 {{NULL, "current-password", NULL}, 478 "password2", "beta", "", ""}, 479 {{NULL, "CURRENT-PASSWORD", NULL}, 480 "password2", "beta", "", ""}, 481 {{NULL, "new-password", NULL}, 482 "", "", "password2", "beta"}, 483 {{NULL, "nEw-PaSsWoRd", NULL}, 484 "", "", "password2", "beta"}}; 485 486 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { 487 SCOPED_TRACE(testing::Message() << "Iteration " << i); 488 489 PasswordFormBuilder builder(kTestFormActionURL); 490 builder.AddPasswordField("pin1", "123456", NULL); 491 builder.AddPasswordField("pin2", "789101", NULL); 492 builder.AddPasswordField("password1", "alpha", cases[i].autocomplete[0]); 493 builder.AddUsernameField("username1", "William", NULL); 494 builder.AddPasswordField("password2", "beta", cases[i].autocomplete[1]); 495 builder.AddUsernameField("username2", "Smith", NULL); 496 builder.AddPasswordField("password3", "gamma", cases[i].autocomplete[2]); 497 builder.AddSubmitButton("submit", true); 498 std::string html = builder.ProduceHTML(); 499 500 scoped_ptr<PasswordForm> password_form; 501 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); 502 ASSERT_TRUE(password_form); 503 504 // In the absence of username autocomplete attributes, the username should 505 // be the text input field before the first password element. 506 // No constellation of password autocomplete attributes should change that. 507 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element); 508 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); 509 EXPECT_THAT(password_form->other_possible_usernames, 510 testing::ElementsAre(base::UTF8ToUTF16("Smith"))); 511 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element), 512 password_form->password_element); 513 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value), 514 password_form->password_value); 515 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element), 516 password_form->new_password_element); 517 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value), 518 password_form->new_password_value); 519 } 520} 521 522TEST_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 534TEST_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 546TEST_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 577TEST_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 593} // namespace autofill 594