Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / components / autofill / content / renderer / password_form_conversion_utils_browsertest.cc
blob771cdc7284ff8b2b5e7e56ba6855ca2d5e2046a1
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.
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/form_autofill_util.h"
10 #include "components/autofill/content/renderer/password_form_conversion_utils.h"
11 #include "components/autofill/core/browser/form_structure.h"
12 #include "components/autofill/core/common/password_form.h"
13 #include "content/public/test/render_view_test.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "third_party/WebKit/public/platform/WebVector.h"
17 #include "third_party/WebKit/public/web/WebDocument.h"
18 #include "third_party/WebKit/public/web/WebFormControlElement.h"
19 #include "third_party/WebKit/public/web/WebFormElement.h"
20 #include "third_party/WebKit/public/web/WebInputElement.h"
21 #include "third_party/WebKit/public/web/WebLocalFrame.h"
23 using blink::WebFormControlElement;
24 using blink::WebFormElement;
25 using blink::WebFrame;
26 using blink::WebInputElement;
27 using blink::WebVector;
29 namespace autofill {
30 namespace {
32 const char kTestFormActionURL[] = "http://cnn.com";
34 // A builder to produce HTML code for a password form composed of the desired
35 // number and kinds of username and password fields.
36 class PasswordFormBuilder {
37 public:
38 // Creates a builder to start composing a new form. The form will have the
39 // specified |action| URL.
40 explicit PasswordFormBuilder(const char* action) {
41 base::StringAppendF(
42 &html_, "<FORM name=\"Test\" action=\"%s\" method=\"post\">", action);
45 // Appends a new text-type field at the end of the form, having the specified
46 // |name_and_id|, |value|, and |autocomplete| attributes. The |autocomplete|
47 // argument can take two special values, namely:
48 // 1.) NULL, causing no autocomplete attribute to be added,
49 // 2.) "", causing an empty attribute (i.e. autocomplete="") to be added.
50 void AddTextField(const char* name_and_id,
51 const char* value,
52 const char* autocomplete) {
53 std::string autocomplete_attribute(autocomplete ?
54 base::StringPrintf("autocomplete=\"%s\"", autocomplete) : "");
55 base::StringAppendF(
56 &html_,
57 "<INPUT type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>",
58 name_and_id, name_and_id, value, autocomplete_attribute.c_str());
61 // Appends a new password-type field at the end of the form, having the
62 // specified |name_and_id|, |value|, and |autocomplete| attributes. Special
63 // values for |autocomplete| are the same as in AddTextField.
64 void AddPasswordField(const char* name_and_id,
65 const char* value,
66 const char* autocomplete) {
67 std::string autocomplete_attribute(autocomplete ?
68 base::StringPrintf("autocomplete=\"%s\"", autocomplete): "");
69 base::StringAppendF(
70 &html_,
71 "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>",
72 name_and_id, name_and_id, value, autocomplete_attribute.c_str());
75 // Appends a disabled text-type field at the end of the form.
76 void AddDisabledUsernameField() {
77 html_ += "<INPUT type=\"text\" disabled/>";
80 // Appends a disabled password-type field at the end of the form.
81 void AddDisabledPasswordField() {
82 html_ += "<INPUT type=\"password\" disabled/>";
85 // Appends a hidden field at the end of the form.
86 void AddHiddenField() { html_ += "<INPUT type=\"hidden\"/>"; }
88 // Appends a new hidden-type field at the end of the form, having the
89 // specified |name_and_id| and |value| attributes.
90 void AddHiddenField(const char* name_and_id,
91 const char* value) {
92 base::StringAppendF(
93 &html_,
94 "<INPUT type=\"hidden\" name=\"%s\" id=\"%s\" value=\"%s\" />",
95 name_and_id, name_and_id, value);
98 // Appends a new submit-type field at the end of the form with the specified
99 // |name|.
100 void AddSubmitButton(const char* name) {
101 base::StringAppendF(
102 &html_,
103 "<INPUT type=\"submit\" name=\"%s\" value=\"Submit\"/>",
104 name);
107 // Returns the HTML code for the form containing the fields that have been
108 // added so far.
109 std::string ProduceHTML() const {
110 return html_ + "</FORM>";
113 private:
114 std::string html_;
116 DISALLOW_COPY_AND_ASSIGN(PasswordFormBuilder);
119 // RenderViewTest-based tests crash on Android
120 // http://crbug.com/187500
121 #if defined(OS_ANDROID)
122 #define MAYBE_PasswordFormConversionUtilsTest \
123 DISABLED_PasswordFormConversionUtilsTest
124 #else
125 #define MAYBE_PasswordFormConversionUtilsTest PasswordFormConversionUtilsTest
126 #endif // defined(OS_ANDROID)
128 class MAYBE_PasswordFormConversionUtilsTest : public content::RenderViewTest {
129 public:
130 MAYBE_PasswordFormConversionUtilsTest() : content::RenderViewTest() {}
131 ~MAYBE_PasswordFormConversionUtilsTest() override {}
133 protected:
134 // Loads the given |html|, retrieves the sole WebFormElement from it, and then
135 // calls CreatePasswordForm(), passing it the |predictions| to convert it into
136 // a |password_form|. Note that ASSERT() can only be used in void functions,
137 // this is why |password_form| is passed in as a pointer to a scoped_ptr.
138 void LoadHTMLAndConvertForm(const std::string& html,
139 scoped_ptr<PasswordForm>* password_form,
140 FormsPredictionsMap* predictions) {
141 WebFormElement form;
142 LoadWebFormFromHTML(html, &form);
144 WebVector<WebFormControlElement> control_elements;
145 form.getFormControlElements(control_elements);
146 for (size_t i = 0; i < control_elements.size(); ++i) {
147 WebInputElement* input_element = toWebInputElement(&control_elements[i]);
148 if (input_element->hasAttribute("set-activated-submit"))
149 input_element->setActivatedSubmit(true);
152 *password_form = CreatePasswordFormFromWebForm(form, nullptr, predictions);
155 // Iterates on the form generated by the |html| and adds the fields and type
156 // predictions corresponding to |predictions_positions| to |predictions|.
157 void SetPredictions(const std::string& html,
158 FormsPredictionsMap* predictions,
159 const std::map<int, PasswordFormFieldPredictionType>&
160 predictions_positions) {
161 WebFormElement form;
162 LoadWebFormFromHTML(html, &form);
164 FormData form_data;
165 ASSERT_TRUE(WebFormElementToFormData(form, WebFormControlElement(),
166 EXTRACT_NONE, &form_data, nullptr));
168 FormStructure form_structure(form_data);
170 int field_index = 0;
171 for (std::vector<AutofillField *>::const_iterator
172 field = form_structure.begin();
173 field != form_structure.end(); ++field, ++field_index) {
174 if (predictions_positions.find(field_index) !=
175 predictions_positions.end()) {
176 (*predictions)[form_data][*(*field)] =
177 predictions_positions.find(field_index)->second;
182 // Loads the given |html| and retrieves the sole WebFormElement from it.
183 void LoadWebFormFromHTML(const std::string& html, WebFormElement* form) {
184 LoadHTML(html.c_str());
186 WebFrame* frame = GetMainFrame();
187 ASSERT_NE(nullptr, frame);
189 WebVector<WebFormElement> forms;
190 frame->document().forms(forms);
191 ASSERT_EQ(1U, forms.size());
193 *form = forms[0];
196 private:
197 DISALLOW_COPY_AND_ASSIGN(MAYBE_PasswordFormConversionUtilsTest);
200 } // namespace
202 TEST_F(MAYBE_PasswordFormConversionUtilsTest, BasicFormAttributes) {
203 PasswordFormBuilder builder(kTestFormActionURL);
204 builder.AddTextField("username", "johnsmith", NULL);
205 builder.AddSubmitButton("inactive_submit");
206 builder.AddSubmitButton("active_submit");
207 builder.AddSubmitButton("inactive_submit2");
208 builder.AddPasswordField("password", "secret", NULL);
209 std::string html = builder.ProduceHTML();
211 scoped_ptr<PasswordForm> password_form;
212 ASSERT_NO_FATAL_FAILURE(
213 LoadHTMLAndConvertForm(html, &password_form, nullptr));
214 ASSERT_TRUE(password_form);
216 EXPECT_EQ("data:", password_form->signon_realm);
217 EXPECT_EQ(GURL(kTestFormActionURL), password_form->action);
218 EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
219 EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
220 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
221 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
222 EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form->scheme);
223 EXPECT_FALSE(password_form->ssl_valid);
224 EXPECT_FALSE(password_form->preferred);
225 EXPECT_FALSE(password_form->blacklisted_by_user);
226 EXPECT_EQ(PasswordForm::TYPE_MANUAL, password_form->type);
229 TEST_F(MAYBE_PasswordFormConversionUtilsTest, DisabledFieldsAreIgnored) {
230 PasswordFormBuilder builder(kTestFormActionURL);
231 builder.AddTextField("username", "johnsmith", NULL);
232 builder.AddDisabledUsernameField();
233 builder.AddDisabledPasswordField();
234 builder.AddPasswordField("password", "secret", NULL);
235 builder.AddSubmitButton("submit");
236 std::string html = builder.ProduceHTML();
238 scoped_ptr<PasswordForm> password_form;
239 ASSERT_NO_FATAL_FAILURE(
240 LoadHTMLAndConvertForm(html, &password_form, nullptr));
241 ASSERT_TRUE(password_form);
242 EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
243 EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
244 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
245 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
248 TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingUsernameFields) {
249 // Each test case consists of a set of parameters to be plugged into the
250 // PasswordFormBuilder below, plus the corresponding expectations.
251 struct TestCase {
252 const char* autocomplete[3];
253 const char* expected_username_element;
254 const char* expected_username_value;
255 const char* expected_other_possible_usernames;
256 } cases[] = {
257 // When no elements are marked with autocomplete='username', the text-type
258 // input field before the first password element should get selected as
259 // the username, and the rest should be marked as alternatives.
260 {{NULL, NULL, NULL}, "username2", "William", "John+Smith"},
261 // When a sole element is marked with autocomplete='username', it should
262 // be treated as the username for sure, with no other_possible_usernames.
263 {{"username", NULL, NULL}, "username1", "John", ""},
264 {{NULL, "username", NULL}, "username2", "William", ""},
265 {{NULL, NULL, "username"}, "username3", "Smith", ""},
266 // When >=2 elements have the attribute, the first should be selected as
267 // the username, and the rest should go to other_possible_usernames.
268 {{"username", "username", NULL}, "username1", "John", "William"},
269 {{NULL, "username", "username"}, "username2", "William", "Smith"},
270 {{"username", NULL, "username"}, "username1", "John", "Smith"},
271 {{"username", "username", "username"}, "username1", "John",
272 "William+Smith"},
273 // When there is an empty autocomplete attribute (i.e. autocomplete=""),
274 // it should have the same effect as having no attribute whatsoever.
275 {{"", "", ""}, "username2", "William", "John+Smith"},
276 {{"", "", "username"}, "username3", "Smith", ""},
277 {{"username", "", "username"}, "username1", "John", "Smith"},
278 // It should not matter if attribute values are upper or mixed case.
279 {{"USERNAME", NULL, "uSeRNaMe"}, "username1", "John", "Smith"},
280 {{"uSeRNaMe", NULL, "USERNAME"}, "username1", "John", "Smith"}};
282 for (size_t i = 0; i < arraysize(cases); ++i) {
283 for (size_t nonempty_username_fields = 0; nonempty_username_fields < 2;
284 ++nonempty_username_fields) {
285 SCOPED_TRACE(testing::Message()
286 << "Iteration " << i << " "
287 << (nonempty_username_fields ? "nonempty" : "empty"));
289 // Repeat each test once with empty, and once with non-empty usernames.
290 // In the former case, no empty other_possible_usernames should be saved.
291 const char* names[3];
292 if (nonempty_username_fields) {
293 names[0] = "John";
294 names[1] = "William";
295 names[2] = "Smith";
296 } else {
297 names[0] = names[1] = names[2] = "";
300 PasswordFormBuilder builder(kTestFormActionURL);
301 builder.AddTextField("username1", names[0], cases[i].autocomplete[0]);
302 builder.AddTextField("username2", names[1], cases[i].autocomplete[1]);
303 builder.AddPasswordField("password", "secret", NULL);
304 builder.AddTextField("username3", names[2], cases[i].autocomplete[2]);
305 builder.AddPasswordField("password2", "othersecret", NULL);
306 builder.AddSubmitButton("submit");
307 std::string html = builder.ProduceHTML();
309 scoped_ptr<PasswordForm> password_form;
310 ASSERT_NO_FATAL_FAILURE(
311 LoadHTMLAndConvertForm(html, &password_form, nullptr));
312 ASSERT_TRUE(password_form);
314 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element),
315 password_form->username_element);
317 if (nonempty_username_fields) {
318 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value),
319 password_form->username_value);
320 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_other_possible_usernames),
321 base::JoinString(password_form->other_possible_usernames,
322 base::ASCIIToUTF16("+")));
323 } else {
324 EXPECT_TRUE(password_form->username_value.empty());
325 EXPECT_TRUE(password_form->other_possible_usernames.empty());
328 // Do a basic sanity check that we are still having a password field.
329 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
330 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
335 TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingTwoPasswordFields) {
336 // Each test case consists of a set of parameters to be plugged into the
337 // PasswordFormBuilder below, plus the corresponding expectations.
338 struct TestCase {
339 const char* password_values[2];
340 const char* expected_password_element;
341 const char* expected_password_value;
342 const char* expected_new_password_element;
343 const char* expected_new_password_value;
344 } cases[] = {
345 // Two non-empty fields with the same value should be treated as a new
346 // password field plus a confirmation field for the new password.
347 {{"alpha", "alpha"}, "", "", "password1", "alpha"},
348 // The same goes if the fields are yet empty: we speculate that we will
349 // identify them as new password fields once they are filled out, and we
350 // want to keep our abstract interpretation of the form less flaky.
351 {{"", ""}, "password1", "", "password2", ""},
352 // Two different values should be treated as a password change form, one
353 // that also asks for the current password, but only once for the new.
354 {{"alpha", ""}, "password1", "alpha", "password2", ""},
355 {{"", "beta"}, "password1", "", "password2", "beta"},
356 {{"alpha", "beta"}, "password1", "alpha", "password2", "beta"}};
358 for (size_t i = 0; i < arraysize(cases); ++i) {
359 SCOPED_TRACE(testing::Message() << "Iteration " << i);
361 PasswordFormBuilder builder(kTestFormActionURL);
362 builder.AddPasswordField("password1", cases[i].password_values[0], NULL);
363 builder.AddTextField("username1", "William", NULL);
364 builder.AddPasswordField("password2", cases[i].password_values[1], NULL);
365 builder.AddTextField("username2", "Smith", NULL);
366 builder.AddSubmitButton("submit");
367 std::string html = builder.ProduceHTML();
369 scoped_ptr<PasswordForm> password_form;
370 ASSERT_NO_FATAL_FAILURE(
371 LoadHTMLAndConvertForm(html, &password_form, nullptr));
372 ASSERT_TRUE(password_form);
374 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element),
375 password_form->password_element);
376 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value),
377 password_form->password_value);
378 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element),
379 password_form->new_password_element);
380 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value),
381 password_form->new_password_value);
383 // Do a basic sanity check that we are still selecting the right username.
384 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
385 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
386 EXPECT_THAT(password_form->other_possible_usernames,
387 testing::ElementsAre(base::UTF8ToUTF16("Smith")));
391 TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingThreePasswordFields) {
392 // Each test case consists of a set of parameters to be plugged into the
393 // PasswordFormBuilder below, plus the corresponding expectations.
394 struct TestCase {
395 const char* password_values[3];
396 const char* expected_password_element;
397 const char* expected_password_value;
398 const char* expected_new_password_element;
399 const char* expected_new_password_value;
400 } cases[] = {
401 // Two fields with the same value, and one different: we should treat this
402 // as a password change form with confirmation for the new password. Note
403 // that we only recognize (current + new + new) and (new + new + current)
404 // without autocomplete attributes.
405 {{"alpha", "", ""}, "password1", "alpha", "password2", ""},
406 {{"", "beta", "beta"}, "password1", "", "password2", "beta"},
407 {{"alpha", "beta", "beta"}, "password1", "alpha", "password2", "beta"},
408 // If confirmed password comes first, assume that the third password
409 // field is related to security question, SSN, or credit card and ignore
410 // it.
411 {{"beta", "beta", "alpha"}, "", "", "password1", "beta"},
412 // If the fields are yet empty, we speculate that we will identify them as
413 // (current + new + new) once they are filled out, so we should classify
414 // them the same for now to keep our abstract interpretation less flaky.
415 {{"", "", ""}, "password1", "", "password2", ""}};
416 // Note: In all other cases, we give up and consider the form invalid.
417 // This is tested in InvalidFormDueToConfusingPasswordFields.
419 for (size_t i = 0; i < arraysize(cases); ++i) {
420 SCOPED_TRACE(testing::Message() << "Iteration " << i);
422 PasswordFormBuilder builder(kTestFormActionURL);
423 builder.AddPasswordField("password1", cases[i].password_values[0], NULL);
424 builder.AddTextField("username1", "William", NULL);
425 builder.AddPasswordField("password2", cases[i].password_values[1], NULL);
426 builder.AddTextField("username2", "Smith", NULL);
427 builder.AddPasswordField("password3", cases[i].password_values[2], NULL);
428 builder.AddSubmitButton("submit");
429 std::string html = builder.ProduceHTML();
431 scoped_ptr<PasswordForm> password_form;
432 ASSERT_NO_FATAL_FAILURE(
433 LoadHTMLAndConvertForm(html, &password_form, nullptr));
434 ASSERT_TRUE(password_form);
436 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element),
437 password_form->password_element);
438 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value),
439 password_form->password_value);
440 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element),
441 password_form->new_password_element);
442 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value),
443 password_form->new_password_value);
445 // Do a basic sanity check that we are still selecting the right username.
446 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
447 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
448 EXPECT_THAT(password_form->other_possible_usernames,
449 testing::ElementsAre(base::UTF8ToUTF16("Smith")));
453 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
454 IdentifyingPasswordFieldsWithAutocompleteAttributes) {
455 // Each test case consists of a set of parameters to be plugged into the
456 // PasswordFormBuilder below, plus the corresponding expectations.
457 struct TestCase {
458 const char* autocomplete[3];
459 const char* expected_password_element;
460 const char* expected_password_value;
461 const char* expected_new_password_element;
462 const char* expected_new_password_value;
463 bool expected_new_password_marked_by_site;
464 } cases[] = {
465 // When there are elements marked with autocomplete='current-password',
466 // but no elements with 'new-password', we should treat the first of the
467 // former kind as the current password, and ignore all other password
468 // fields, assuming they are not intentionally not marked. They might be
469 // for other purposes, such as PINs, OTPs, and the like. Actual values in
470 // the password fields should be ignored in all cases below.
471 {{"current-password", NULL, NULL},
472 "password1", "alpha", "", "", false},
473 {{NULL, "current-password", NULL},
474 "password2", "beta", "", "", false},
475 {{NULL, NULL, "current-password"},
476 "password3", "gamma", "", "", false},
477 {{NULL, "current-password", "current-password"},
478 "password2", "beta", "", "", false},
479 {{"current-password", NULL, "current-password"},
480 "password1", "alpha", "", "", false},
481 {{"current-password", "current-password", NULL},
482 "password1", "alpha", "", "", false},
483 {{"current-password", "current-password", "current-password"},
484 "password1", "alpha", "", "", false},
485 // The same goes vice versa for autocomplete='new-password'.
486 {{"new-password", NULL, NULL},
487 "", "", "password1", "alpha", true},
488 {{NULL, "new-password", NULL},
489 "", "", "password2", "beta", true},
490 {{NULL, NULL, "new-password"},
491 "", "", "password3", "gamma", true},
492 {{NULL, "new-password", "new-password"},
493 "", "", "password2", "beta", true},
494 {{"new-password", NULL, "new-password"},
495 "", "", "password1", "alpha", true},
496 {{"new-password", "new-password", NULL},
497 "", "", "password1", "alpha", true},
498 {{"new-password", "new-password", "new-password"},
499 "", "", "password1", "alpha", true},
500 // When there is one element marked with autocomplete='current-password',
501 // and one with 'new-password', just comply, regardless of their order.
502 // Ignore the unmarked password field(s) for the same reason as above.
503 {{"current-password", "new-password", NULL},
504 "password1", "alpha", "password2", "beta", true},
505 {{"current-password", NULL, "new-password"},
506 "password1", "alpha", "password3", "gamma", true},
507 {{NULL, "current-password", "new-password"},
508 "password2", "beta", "password3", "gamma", true},
509 {{"new-password", "current-password", NULL},
510 "password2", "beta", "password1", "alpha", true},
511 {{"new-password", NULL, "current-password"},
512 "password3", "gamma", "password1", "alpha", true},
513 {{NULL, "new-password", "current-password"},
514 "password3", "gamma", "password2", "beta", true},
515 // In case of duplicated elements of either kind, go with the first one of
516 // its kind.
517 {{"current-password", "current-password", "new-password"},
518 "password1", "alpha", "password3", "gamma", true},
519 {{"current-password", "new-password", "current-password"},
520 "password1", "alpha", "password2", "beta", true},
521 {{"new-password", "current-password", "current-password"},
522 "password2", "beta", "password1", "alpha", true},
523 {{"current-password", "new-password", "new-password"},
524 "password1", "alpha", "password2", "beta", true},
525 {{"new-password", "current-password", "new-password"},
526 "password2", "beta", "password1", "alpha", true},
527 {{"new-password", "new-password", "current-password"},
528 "password3", "gamma", "password1", "alpha", true},
529 // When there is an empty autocomplete attribute (i.e. autocomplete=""),
530 // it should have the same effect as having no attribute whatsoever.
531 {{"current-password", "", ""},
532 "password1", "alpha", "", "", false},
533 {{"", "", "new-password"},
534 "", "", "password3", "gamma", true},
535 {{"", "new-password", ""},
536 "", "", "password2", "beta", true},
537 {{"", "current-password", "current-password"},
538 "password2", "beta", "", "", false},
539 {{"new-password", "", "new-password"},
540 "", "", "password1", "alpha", true},
541 {{"new-password", "", "current-password"},
542 "password3", "gamma", "password1", "alpha", true},
543 // It should not matter if attribute values are upper or mixed case.
544 {{NULL, "current-password", NULL},
545 "password2", "beta", "", "", false},
546 {{NULL, "CURRENT-PASSWORD", NULL},
547 "password2", "beta", "", "", false},
548 {{NULL, "new-password", NULL},
549 "", "", "password2", "beta", true},
550 {{NULL, "nEw-PaSsWoRd", NULL},
551 "", "", "password2", "beta", true}};
553 for (size_t i = 0; i < arraysize(cases); ++i) {
554 SCOPED_TRACE(testing::Message() << "Iteration " << i);
556 PasswordFormBuilder builder(kTestFormActionURL);
557 builder.AddPasswordField("pin1", "123456", NULL);
558 builder.AddPasswordField("pin2", "789101", NULL);
559 builder.AddPasswordField("password1", "alpha", cases[i].autocomplete[0]);
560 builder.AddTextField("username1", "William", NULL);
561 builder.AddPasswordField("password2", "beta", cases[i].autocomplete[1]);
562 builder.AddTextField("username2", "Smith", NULL);
563 builder.AddPasswordField("password3", "gamma", cases[i].autocomplete[2]);
564 builder.AddSubmitButton("submit");
565 std::string html = builder.ProduceHTML();
567 scoped_ptr<PasswordForm> password_form;
568 ASSERT_NO_FATAL_FAILURE(
569 LoadHTMLAndConvertForm(html, &password_form, nullptr));
570 ASSERT_TRUE(password_form);
572 // In the absence of username autocomplete attributes, the username should
573 // be the text input field before the first password element.
574 // No constellation of password autocomplete attributes should change that.
575 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
576 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
577 EXPECT_THAT(password_form->other_possible_usernames,
578 testing::ElementsAre(base::UTF8ToUTF16("Smith")));
579 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element),
580 password_form->password_element);
581 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value),
582 password_form->password_value);
583 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element),
584 password_form->new_password_element);
585 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value),
586 password_form->new_password_value);
587 EXPECT_EQ(cases[i].expected_new_password_marked_by_site,
588 password_form->new_password_marked_by_site);
592 TEST_F(MAYBE_PasswordFormConversionUtilsTest, InvalidFormDueToBadActionURL) {
593 PasswordFormBuilder builder("invalid_target");
594 builder.AddTextField("username", "JohnSmith", NULL);
595 builder.AddSubmitButton("submit");
596 builder.AddPasswordField("password", "secret", NULL);
597 std::string html = builder.ProduceHTML();
599 scoped_ptr<PasswordForm> password_form;
600 ASSERT_NO_FATAL_FAILURE(
601 LoadHTMLAndConvertForm(html, &password_form, nullptr));
602 EXPECT_FALSE(password_form);
605 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
606 InvalidFormDueToNoPasswordFields) {
607 PasswordFormBuilder builder(kTestFormActionURL);
608 builder.AddTextField("username1", "John", NULL);
609 builder.AddTextField("username2", "Smith", NULL);
610 builder.AddSubmitButton("submit");
611 std::string html = builder.ProduceHTML();
613 scoped_ptr<PasswordForm> password_form;
614 ASSERT_NO_FATAL_FAILURE(
615 LoadHTMLAndConvertForm(html, &password_form, nullptr));
616 EXPECT_FALSE(password_form);
619 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
620 InvalidFormsDueToConfusingPasswordFields) {
621 // Each test case consists of a set of parameters to be plugged into the
622 // PasswordFormBuilder below.
623 const char* cases[][3] = {
624 // No autocomplete attributes to guide us, and we see:
625 // * three password values that are all different,
626 // * three password values that are all the same;
627 // * three password values with the first and last matching.
628 // In any case, we should just give up on this form.
629 {"alpha", "beta", "gamma"},
630 {"alpha", "alpha", "alpha"},
631 {"alpha", "beta", "alpha"}};
633 for (size_t i = 0; i < arraysize(cases); ++i) {
634 SCOPED_TRACE(testing::Message() << "Iteration " << i);
636 PasswordFormBuilder builder(kTestFormActionURL);
637 builder.AddTextField("username1", "John", NULL);
638 builder.AddPasswordField("password1", cases[i][0], NULL);
639 builder.AddPasswordField("password2", cases[i][1], NULL);
640 builder.AddPasswordField("password3", cases[i][2], NULL);
641 builder.AddSubmitButton("submit");
642 std::string html = builder.ProduceHTML();
644 scoped_ptr<PasswordForm> password_form;
645 ASSERT_NO_FATAL_FAILURE(
646 LoadHTMLAndConvertForm(html, &password_form, nullptr));
647 EXPECT_FALSE(password_form);
651 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
652 InvalidFormDueToTooManyPasswordFieldsWithoutAutocompleteAttributes) {
653 PasswordFormBuilder builder(kTestFormActionURL);
654 builder.AddTextField("username1", "John", NULL);
655 builder.AddPasswordField("password1", "alpha", NULL);
656 builder.AddPasswordField("password2", "alpha", NULL);
657 builder.AddPasswordField("password3", "alpha", NULL);
658 builder.AddPasswordField("password4", "alpha", NULL);
659 builder.AddSubmitButton("submit");
660 std::string html = builder.ProduceHTML();
662 scoped_ptr<PasswordForm> password_form;
663 ASSERT_NO_FATAL_FAILURE(
664 LoadHTMLAndConvertForm(html, &password_form, nullptr));
665 EXPECT_FALSE(password_form);
668 TEST_F(MAYBE_PasswordFormConversionUtilsTest, LayoutClassificationLogin) {
669 PasswordFormBuilder builder(kTestFormActionURL);
670 builder.AddHiddenField();
671 builder.AddTextField("username", "", nullptr);
672 builder.AddPasswordField("password", "", nullptr);
673 builder.AddSubmitButton("submit");
674 std::string login_html = builder.ProduceHTML();
676 scoped_ptr<PasswordForm> login_form;
677 ASSERT_NO_FATAL_FAILURE(
678 LoadHTMLAndConvertForm(login_html, &login_form, nullptr));
679 ASSERT_TRUE(login_form);
680 EXPECT_EQ(PasswordForm::Layout::LAYOUT_OTHER, login_form->layout);
683 TEST_F(MAYBE_PasswordFormConversionUtilsTest, LayoutClassificationSignup) {
684 PasswordFormBuilder builder(kTestFormActionURL);
685 builder.AddTextField("someotherfield", "", nullptr);
686 builder.AddTextField("username", "", nullptr);
687 builder.AddPasswordField("new_password", "", nullptr);
688 builder.AddHiddenField();
689 builder.AddPasswordField("new_password2", "", nullptr);
690 builder.AddSubmitButton("submit");
691 std::string signup_html = builder.ProduceHTML();
693 scoped_ptr<PasswordForm> signup_form;
694 ASSERT_NO_FATAL_FAILURE(
695 LoadHTMLAndConvertForm(signup_html, &signup_form, nullptr));
696 ASSERT_TRUE(signup_form);
697 EXPECT_EQ(PasswordForm::Layout::LAYOUT_OTHER, signup_form->layout);
700 TEST_F(MAYBE_PasswordFormConversionUtilsTest, LayoutClassificationChange) {
701 PasswordFormBuilder builder(kTestFormActionURL);
702 builder.AddTextField("username", "", nullptr);
703 builder.AddPasswordField("old_password", "", nullptr);
704 builder.AddHiddenField();
705 builder.AddPasswordField("new_password", "", nullptr);
706 builder.AddPasswordField("new_password2", "", nullptr);
707 builder.AddSubmitButton("submit");
708 std::string change_html = builder.ProduceHTML();
710 scoped_ptr<PasswordForm> change_form;
711 ASSERT_NO_FATAL_FAILURE(
712 LoadHTMLAndConvertForm(change_html, &change_form, nullptr));
713 ASSERT_TRUE(change_form);
714 EXPECT_EQ(PasswordForm::Layout::LAYOUT_OTHER, change_form->layout);
717 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
718 LayoutClassificationLoginPlusSignup_A) {
719 PasswordFormBuilder builder(kTestFormActionURL);
720 builder.AddTextField("username", "", nullptr);
721 builder.AddHiddenField();
722 builder.AddPasswordField("password", "", nullptr);
723 builder.AddTextField("username2", "", nullptr);
724 builder.AddTextField("someotherfield", "", nullptr);
725 builder.AddPasswordField("new_password", "", nullptr);
726 builder.AddPasswordField("new_password2", "", nullptr);
727 builder.AddHiddenField();
728 builder.AddSubmitButton("submit");
729 std::string login_plus_signup_html = builder.ProduceHTML();
731 scoped_ptr<PasswordForm> login_plus_signup_form;
732 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(
733 login_plus_signup_html, &login_plus_signup_form, nullptr));
734 ASSERT_TRUE(login_plus_signup_form);
735 EXPECT_EQ(PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP,
736 login_plus_signup_form->layout);
739 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
740 LayoutClassificationLoginPlusSignup_B) {
741 PasswordFormBuilder builder(kTestFormActionURL);
742 builder.AddTextField("username", "", nullptr);
743 builder.AddHiddenField();
744 builder.AddPasswordField("password", "", nullptr);
745 builder.AddTextField("username2", "", nullptr);
746 builder.AddTextField("someotherfield", "", nullptr);
747 builder.AddPasswordField("new_password", "", nullptr);
748 builder.AddTextField("someotherfield2", "", nullptr);
749 builder.AddHiddenField();
750 builder.AddSubmitButton("submit");
751 std::string login_plus_signup_html = builder.ProduceHTML();
753 scoped_ptr<PasswordForm> login_plus_signup_form;
754 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(
755 login_plus_signup_html, &login_plus_signup_form, nullptr));
756 ASSERT_TRUE(login_plus_signup_form);
757 EXPECT_EQ(PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP,
758 login_plus_signup_form->layout);
761 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
762 CreditCardNumberWithTypePasswordForm) {
763 PasswordFormBuilder builder(kTestFormActionURL);
764 builder.AddTextField("Credit card owner name", "John Smith", nullptr);
765 builder.AddPasswordField("Credit card number", "0000 0000 0000 0000",
766 nullptr);
767 builder.AddTextField("cvc", "000", nullptr);
768 builder.AddSubmitButton("submit");
769 std::string html = builder.ProduceHTML();
771 std::map<int, PasswordFormFieldPredictionType> predictions_positions;
772 predictions_positions[1] = PREDICTION_NOT_PASSWORD;
774 FormsPredictionsMap predictions;
775 SetPredictions(html, &predictions, predictions_positions);
777 scoped_ptr<PasswordForm> password_form;
778 LoadHTMLAndConvertForm(html, &password_form, &predictions);
779 EXPECT_FALSE(password_form);
782 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
783 CreditCardVerificationNumberWithTypePasswordForm) {
784 PasswordFormBuilder builder(kTestFormActionURL);
785 builder.AddTextField("Credit card owner name", "John Smith", nullptr);
786 builder.AddTextField("Credit card number", "0000 0000 0000 0000", nullptr);
787 builder.AddPasswordField("cvc", "000", nullptr);
788 builder.AddSubmitButton("submit");
789 std::string html = builder.ProduceHTML();
791 std::map<int, PasswordFormFieldPredictionType> predictions_positions;
792 predictions_positions[2] = PREDICTION_NOT_PASSWORD;
794 FormsPredictionsMap predictions;
795 SetPredictions(html, &predictions, predictions_positions);
797 scoped_ptr<PasswordForm> password_form;
798 LoadHTMLAndConvertForm(html, &password_form, &predictions);
799 EXPECT_FALSE(password_form);
802 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
803 CreditCardNumberWithTypePasswordFormWithAutocomplete) {
804 PasswordFormBuilder builder(kTestFormActionURL);
805 builder.AddTextField("Credit card owner name", "John Smith", nullptr);
806 builder.AddPasswordField("Credit card number", "0000 0000 0000 0000",
807 "current-password");
808 builder.AddTextField("cvc", "000", nullptr);
809 builder.AddSubmitButton("submit");
810 std::string html = builder.ProduceHTML();
812 std::map<int, PasswordFormFieldPredictionType> predictions_positions;
813 predictions_positions[1] = PREDICTION_NOT_PASSWORD;
815 FormsPredictionsMap predictions;
816 SetPredictions(html, &predictions, predictions_positions);
818 scoped_ptr<PasswordForm> password_form;
819 LoadHTMLAndConvertForm(html, &password_form, &predictions);
820 EXPECT_TRUE(password_form);
823 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
824 CreditCardVerificationNumberWithTypePasswordFormWithAutocomplete) {
825 PasswordFormBuilder builder(kTestFormActionURL);
826 builder.AddTextField("Credit card owner name", "John Smith", nullptr);
827 builder.AddTextField("Credit card number", "0000 0000 0000 0000", nullptr);
828 builder.AddPasswordField("cvc", "000", "new-password");
829 builder.AddSubmitButton("submit");
830 std::string html = builder.ProduceHTML();
832 std::map<int, PasswordFormFieldPredictionType> predictions_positions;
833 predictions_positions[2] = PREDICTION_NOT_PASSWORD;
835 FormsPredictionsMap predictions;
836 SetPredictions(html, &predictions, predictions_positions);
838 scoped_ptr<PasswordForm> password_form;
839 LoadHTMLAndConvertForm(html, &password_form, &predictions);
840 EXPECT_TRUE(password_form);
843 TEST_F(MAYBE_PasswordFormConversionUtilsTest, IsGaiaReauthFormIgnored) {
844 scoped_ptr<PasswordFormBuilder> builder;
845 FormsPredictionsMap predictions;
846 WebFormElement form;
847 WebVector<WebFormControlElement> control_elements;
849 // A common password form is parsed successfully.
850 builder.reset(new PasswordFormBuilder("https://example.com"));
851 builder->AddTextField("username", "", nullptr);
852 builder->AddPasswordField("password", "", nullptr);
853 std::string html = builder->ProduceHTML();
854 LoadWebFormFromHTML(html, &form);
855 form.getFormControlElements(control_elements);
856 EXPECT_FALSE(IsGaiaReauthenticationForm(
857 GURL("https://example.com"), control_elements));
859 // A common password form, even if it appears on a GAIA reauth url,
860 // is parsed successfully.
861 builder.reset(new PasswordFormBuilder("https://accounts.google.com"));
862 builder->AddTextField("username", "", nullptr);
863 builder->AddPasswordField("password", "", nullptr);
864 html = builder->ProduceHTML();
865 LoadWebFormFromHTML(html, &form);
866 form.getFormControlElements(control_elements);
867 EXPECT_FALSE(IsGaiaReauthenticationForm(
868 GURL("https://accounts.google.com"), control_elements));
870 // Not a transactional reauth.
871 builder.reset(new PasswordFormBuilder("https://accounts.google.com"));
872 builder->AddTextField("username", "", nullptr);
873 builder->AddPasswordField("password", "", nullptr);
874 builder->AddHiddenField(
875 "continue", "https://passwords.google.com/settings");
876 html = builder->ProduceHTML();
877 LoadWebFormFromHTML(html, &form);
878 form.getFormControlElements(control_elements);
879 EXPECT_FALSE(IsGaiaReauthenticationForm(
880 GURL("https://accounts.google.com"), control_elements));
882 // A reauth form that is not for a password site is parsed successfuly.
883 builder.reset(new PasswordFormBuilder("https://accounts.google.com"));
884 builder->AddTextField("username", "", nullptr);
885 builder->AddPasswordField("password", "", nullptr);
886 builder->AddHiddenField("rart", "");
887 builder->AddHiddenField("continue", "https://mail.google.com");
888 html = builder->ProduceHTML();
889 LoadWebFormFromHTML(html, &form);
890 form.getFormControlElements(control_elements);
891 EXPECT_FALSE(IsGaiaReauthenticationForm(
892 GURL("https://accounts.google.com"), control_elements));
894 // A reauth form for a password site is ignored.
895 builder.reset(new PasswordFormBuilder("https://accounts.google.com"));
896 builder->AddTextField("username", "", nullptr);
897 builder->AddPasswordField("password", "", nullptr);
898 builder->AddHiddenField("rart", "");
899 builder->AddHiddenField("continue", "https://passwords.google.com");
900 html = builder->ProduceHTML();
901 LoadWebFormFromHTML(html, &form);
902 form.getFormControlElements(control_elements);
903 EXPECT_TRUE(IsGaiaReauthenticationForm(
904 GURL("https://accounts.google.com"), control_elements));
906 // Password site is inaccesible via HTTP, but because of HSTS the following
907 // link should still continue to https://passwords.google.com.
908 builder.reset(new PasswordFormBuilder("https://accounts.google.com"));
909 builder->AddTextField("username", "", nullptr);
910 builder->AddPasswordField("password", "", nullptr);
911 builder->AddHiddenField("rart", "");
912 builder->AddHiddenField("continue", "http://passwords.google.com");
913 html = builder->ProduceHTML();
914 LoadWebFormFromHTML(html, &form);
915 form.getFormControlElements(control_elements);
916 EXPECT_TRUE(IsGaiaReauthenticationForm(
917 GURL("https://accounts.google.com"), control_elements));
919 // Make sure testing sites are disabled as well.
920 builder.reset(new PasswordFormBuilder("https://accounts.google.com"));
921 builder->AddTextField("username", "", nullptr);
922 builder->AddPasswordField("password", "", nullptr);
923 builder->AddHiddenField("rart", "");
924 builder->AddHiddenField(
925 "continue", "https://passwords-ac-testing.corp.google.com/settings");
926 html = builder->ProduceHTML();
927 LoadWebFormFromHTML(html, &form);
928 form.getFormControlElements(control_elements);
929 EXPECT_TRUE(IsGaiaReauthenticationForm(
930 GURL("https://accounts.google.com"), control_elements));
932 // Specifying default port doesn't change anything.
933 builder.reset(new PasswordFormBuilder("https://accounts.google.com"));
934 builder->AddTextField("username", "", nullptr);
935 builder->AddPasswordField("password", "", nullptr);
936 builder->AddHiddenField("rart", "");
937 builder->AddHiddenField("continue", "passwords.google.com:443");
938 html = builder->ProduceHTML();
939 LoadWebFormFromHTML(html, &form);
940 form.getFormControlElements(control_elements);
941 EXPECT_TRUE(IsGaiaReauthenticationForm(
942 GURL("https://accounts.google.com"), control_elements));
944 // Encoded URL is considered the same.
945 builder.reset(new PasswordFormBuilder("https://accounts.google.com"));
946 builder->AddTextField("username", "", nullptr);
947 builder->AddPasswordField("password", "", nullptr);
948 builder->AddHiddenField("rart", "");
949 builder->AddHiddenField(
950 "continue", "https://passwords.%67oogle.com/settings&rart=123");
951 html = builder->ProduceHTML();
952 LoadWebFormFromHTML(html, &form);
953 form.getFormControlElements(control_elements);
954 EXPECT_TRUE(IsGaiaReauthenticationForm(
955 GURL("https://accounts.google.com"), control_elements));
957 // Fully qualified domain name is considered a different hostname by GURL.
958 // Ideally this would not be the case, but this quirk can be avoided by
959 // verification on the server. This test is simply documentation of this
960 // behavior.
961 builder.reset(new PasswordFormBuilder("https://accounts.google.com"));
962 builder->AddTextField("username", "", nullptr);
963 builder->AddPasswordField("password", "", nullptr);
964 builder->AddHiddenField("rart", "");
965 builder->AddHiddenField("continue", "https://passwords.google.com./settings");
966 html = builder->ProduceHTML();
967 LoadWebFormFromHTML(html, &form);
968 form.getFormControlElements(control_elements);
969 EXPECT_TRUE(IsGaiaReauthenticationForm(
970 GURL("https://accounts.google.com"), control_elements));
972 // A correctly looking form, but on a different page.
973 builder.reset(new PasswordFormBuilder("https://google.com"));
974 builder->AddTextField("username", "", nullptr);
975 builder->AddPasswordField("password", "", nullptr);
976 builder->AddHiddenField("rart", "");
977 builder->AddHiddenField("continue", "https://passwords.google.com");
978 html = builder->ProduceHTML();
979 LoadWebFormFromHTML(html, &form);
980 form.getFormControlElements(control_elements);
981 EXPECT_FALSE(IsGaiaReauthenticationForm(
982 GURL("https://google.com"), control_elements));
985 } // namespace autofill