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/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"
21 using blink::WebFormControlElement
;
22 using blink::WebFormElement
;
23 using blink::WebFrame
;
24 using blink::WebInputElement
;
25 using blink::WebVector
;
30 const char kTestFormActionURL
[] = "http://cnn.com";
32 // A builder to produce HTML code for a password form composed of the desired
33 // number and kinds of username and password fields.
34 class PasswordFormBuilder
{
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
) {
40 &html_
, "<FORM name=\"Test\" action=\"%s\" method=\"post\">", action
);
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
,
50 const char* autocomplete
) {
51 std::string
autocomplete_attribute(autocomplete
?
52 base::StringPrintf("autocomplete=\"%s\"", autocomplete
) : "");
55 "<INPUT type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>",
56 name_and_id
, name_and_id
, value
, autocomplete_attribute
.c_str());
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
,
64 const char* autocomplete
) {
65 std::string
autocomplete_attribute(autocomplete
?
66 base::StringPrintf("autocomplete=\"%s\"", autocomplete
): "");
69 "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>",
70 name_and_id
, name_and_id
, value
, autocomplete_attribute
.c_str());
73 // Appends a disabled text-type field at the end of the form.
74 void AddDisabledUsernameField() {
75 html_
+= "<INPUT type=\"text\" disabled/>";
78 // Appends a disabled password-type field at the end of the form.
79 void AddDisabledPasswordField() {
80 html_
+= "<INPUT type=\"password\" disabled/>";
83 // Appends a hidden field at the end of the form.
84 void AddHiddenField() { html_
+= "<INPUT type=\"hidden\"/>"; }
86 // Appends a new submit-type field at the end of the form with the specified
87 // |name|. If |activated| is true, the test will emulate as if this button
88 // were used to submit the form.
89 void AddSubmitButton(const char* name
, bool activated
) {
92 "<INPUT type=\"submit\" name=\"%s\" value=\"Submit\" %s/>",
93 name
, activated
? "set-activated-submit" : "");
96 // Returns the HTML code for the form containing the fields that have been
98 std::string
ProduceHTML() const {
99 return html_
+ "</FORM>";
105 DISALLOW_COPY_AND_ASSIGN(PasswordFormBuilder
);
108 // RenderViewTest-based tests crash on Android
109 // http://crbug.com/187500
110 #if defined(OS_ANDROID)
111 #define MAYBE_PasswordFormConversionUtilsTest \
112 DISABLED_PasswordFormConversionUtilsTest
114 #define MAYBE_PasswordFormConversionUtilsTest PasswordFormConversionUtilsTest
115 #endif // defined(OS_ANDROID)
117 class MAYBE_PasswordFormConversionUtilsTest
: public content::RenderViewTest
{
119 MAYBE_PasswordFormConversionUtilsTest() : content::RenderViewTest() {}
120 ~MAYBE_PasswordFormConversionUtilsTest() override
{}
123 // Loads the given |html|, retrieves the sole WebFormElement from it, and then
124 // calls CreatePasswordForm() to convert it into a |password_form|. Note that
125 // ASSERT() can only be used in void functions, this is why |password_form| is
126 // passed in as a pointer to a scoped_ptr.
127 void LoadHTMLAndConvertForm(const std::string
& html
,
128 scoped_ptr
<PasswordForm
>* password_form
) {
129 LoadHTML(html
.c_str());
131 WebFrame
* frame
= GetMainFrame();
132 ASSERT_NE(static_cast<WebFrame
*>(NULL
), frame
);
134 WebVector
<WebFormElement
> forms
;
135 frame
->document().forms(forms
);
136 ASSERT_EQ(1U, forms
.size());
138 WebVector
<WebFormControlElement
> control_elements
;
139 forms
[0].getFormControlElements(control_elements
);
140 for (size_t i
= 0; i
< control_elements
.size(); ++i
) {
141 WebInputElement
* input_element
= toWebInputElement(&control_elements
[i
]);
142 if (input_element
->hasAttribute("set-activated-submit"))
143 input_element
->setActivatedSubmit(true);
146 *password_form
= CreatePasswordForm(forms
[0], nullptr, nullptr);
150 DISALLOW_COPY_AND_ASSIGN(MAYBE_PasswordFormConversionUtilsTest
);
155 TEST_F(MAYBE_PasswordFormConversionUtilsTest
, BasicFormAttributes
) {
156 PasswordFormBuilder
builder(kTestFormActionURL
);
157 builder
.AddUsernameField("username", "johnsmith", NULL
);
158 builder
.AddSubmitButton("inactive_submit", false);
159 builder
.AddSubmitButton("active_submit", true);
160 builder
.AddSubmitButton("inactive_submit2", false);
161 builder
.AddPasswordField("password", "secret", NULL
);
162 std::string html
= builder
.ProduceHTML();
164 scoped_ptr
<PasswordForm
> password_form
;
165 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
166 ASSERT_TRUE(password_form
);
168 EXPECT_EQ("data:", password_form
->signon_realm
);
169 EXPECT_EQ(GURL(kTestFormActionURL
), password_form
->action
);
170 EXPECT_EQ(base::UTF8ToUTF16("active_submit"), password_form
->submit_element
);
171 EXPECT_EQ(base::UTF8ToUTF16("username"), password_form
->username_element
);
172 EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form
->username_value
);
173 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form
->password_element
);
174 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form
->password_value
);
175 EXPECT_EQ(PasswordForm::SCHEME_HTML
, password_form
->scheme
);
176 EXPECT_FALSE(password_form
->ssl_valid
);
177 EXPECT_FALSE(password_form
->preferred
);
178 EXPECT_FALSE(password_form
->blacklisted_by_user
);
179 EXPECT_EQ(PasswordForm::TYPE_MANUAL
, password_form
->type
);
182 TEST_F(MAYBE_PasswordFormConversionUtilsTest
, DisabledFieldsAreIgnored
) {
183 PasswordFormBuilder
builder(kTestFormActionURL
);
184 builder
.AddUsernameField("username", "johnsmith", NULL
);
185 builder
.AddDisabledUsernameField();
186 builder
.AddDisabledPasswordField();
187 builder
.AddPasswordField("password", "secret", NULL
);
188 builder
.AddSubmitButton("submit", true);
189 std::string html
= builder
.ProduceHTML();
191 scoped_ptr
<PasswordForm
> password_form
;
192 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
193 ASSERT_TRUE(password_form
);
194 EXPECT_EQ(base::UTF8ToUTF16("username"), password_form
->username_element
);
195 EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form
->username_value
);
196 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form
->password_element
);
197 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form
->password_value
);
200 TEST_F(MAYBE_PasswordFormConversionUtilsTest
, IdentifyingUsernameFields
) {
201 // Each test case consists of a set of parameters to be plugged into the
202 // PasswordFormBuilder below, plus the corresponding expectations.
204 const char* autocomplete
[3];
205 const char* expected_username_element
;
206 const char* expected_username_value
;
207 const char* expected_other_possible_usernames
;
209 // When no elements are marked with autocomplete='username', the text-type
210 // input field before the first password element should get selected as
211 // the username, and the rest should be marked as alternatives.
212 {{NULL
, NULL
, NULL
}, "username2", "William", "John+Smith"},
213 // When a sole element is marked with autocomplete='username', it should
214 // be treated as the username for sure, with no other_possible_usernames.
215 {{"username", NULL
, NULL
}, "username1", "John", ""},
216 {{NULL
, "username", NULL
}, "username2", "William", ""},
217 {{NULL
, NULL
, "username"}, "username3", "Smith", ""},
218 // When >=2 elements have the attribute, the first should be selected as
219 // the username, and the rest should go to other_possible_usernames.
220 {{"username", "username", NULL
}, "username1", "John", "William"},
221 {{NULL
, "username", "username"}, "username2", "William", "Smith"},
222 {{"username", NULL
, "username"}, "username1", "John", "Smith"},
223 {{"username", "username", "username"}, "username1", "John",
225 // When there is an empty autocomplete attribute (i.e. autocomplete=""),
226 // it should have the same effect as having no attribute whatsoever.
227 {{"", "", ""}, "username2", "William", "John+Smith"},
228 {{"", "", "username"}, "username3", "Smith", ""},
229 {{"username", "", "username"}, "username1", "John", "Smith"},
230 // It should not matter if attribute values are upper or mixed case.
231 {{"USERNAME", NULL
, "uSeRNaMe"}, "username1", "John", "Smith"},
232 {{"uSeRNaMe", NULL
, "USERNAME"}, "username1", "John", "Smith"}};
234 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
235 for (size_t nonempty_username_fields
= 0; nonempty_username_fields
< 2;
236 ++nonempty_username_fields
) {
237 SCOPED_TRACE(testing::Message()
238 << "Iteration " << i
<< " "
239 << (nonempty_username_fields
? "nonempty" : "empty"));
241 // Repeat each test once with empty, and once with non-empty usernames.
242 // In the former case, no empty other_possible_usernames should be saved.
243 const char* names
[3];
244 if (nonempty_username_fields
) {
246 names
[1] = "William";
249 names
[0] = names
[1] = names
[2] = "";
252 PasswordFormBuilder
builder(kTestFormActionURL
);
253 builder
.AddUsernameField("username1", names
[0], cases
[i
].autocomplete
[0]);
254 builder
.AddUsernameField("username2", names
[1], cases
[i
].autocomplete
[1]);
255 builder
.AddPasswordField("password", "secret", NULL
);
256 builder
.AddUsernameField("username3", names
[2], cases
[i
].autocomplete
[2]);
257 builder
.AddPasswordField("password2", "othersecret", NULL
);
258 builder
.AddSubmitButton("submit", true);
259 std::string html
= builder
.ProduceHTML();
261 scoped_ptr
<PasswordForm
> password_form
;
262 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
263 ASSERT_TRUE(password_form
);
265 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_username_element
),
266 password_form
->username_element
);
268 if (nonempty_username_fields
) {
269 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_username_value
),
270 password_form
->username_value
);
271 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_other_possible_usernames
),
272 JoinString(password_form
->other_possible_usernames
, '+'));
274 EXPECT_TRUE(password_form
->username_value
.empty());
275 EXPECT_TRUE(password_form
->other_possible_usernames
.empty());
278 // Do a basic sanity check that we are still having a password field.
279 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form
->password_element
);
280 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form
->password_value
);
285 TEST_F(MAYBE_PasswordFormConversionUtilsTest
, IdentifyingTwoPasswordFields
) {
286 // Each test case consists of a set of parameters to be plugged into the
287 // PasswordFormBuilder below, plus the corresponding expectations.
289 const char* password_values
[2];
290 const char* expected_password_element
;
291 const char* expected_password_value
;
292 const char* expected_new_password_element
;
293 const char* expected_new_password_value
;
295 // Twp non-empty fields with the same value should be treated as a new
296 // password field plus a confirmation field for the new password.
297 {{"alpha", "alpha"}, "", "", "password1", "alpha"},
298 // The same goes if the fields are yet empty: we speculate that we will
299 // identify them as new password fields once they are filled out, and we
300 // want to keep our abstract interpretation of the form less flaky.
301 {{"", ""}, "", "", "password1", ""},
302 // Two different values should be treated as a password change form, one
303 // that also asks for the current password, but only once for the new.
304 {{"alpha", ""}, "password1", "alpha", "password2", ""},
305 {{"", "beta"}, "password1", "", "password2", "beta"},
306 {{"alpha", "beta"}, "password1", "alpha", "password2", "beta"}};
308 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
309 SCOPED_TRACE(testing::Message() << "Iteration " << i
);
311 PasswordFormBuilder
builder(kTestFormActionURL
);
312 builder
.AddPasswordField("password1", cases
[i
].password_values
[0], NULL
);
313 builder
.AddUsernameField("username1", "William", NULL
);
314 builder
.AddPasswordField("password2", cases
[i
].password_values
[1], NULL
);
315 builder
.AddUsernameField("username2", "Smith", NULL
);
316 builder
.AddSubmitButton("submit", true);
317 std::string html
= builder
.ProduceHTML();
319 scoped_ptr
<PasswordForm
> password_form
;
320 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
321 ASSERT_TRUE(password_form
);
323 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_password_element
),
324 password_form
->password_element
);
325 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_password_value
),
326 password_form
->password_value
);
327 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_new_password_element
),
328 password_form
->new_password_element
);
329 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_new_password_value
),
330 password_form
->new_password_value
);
332 // Do a basic sanity check that we are still selecting the right username.
333 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form
->username_element
);
334 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form
->username_value
);
335 EXPECT_THAT(password_form
->other_possible_usernames
,
336 testing::ElementsAre(base::UTF8ToUTF16("Smith")));
340 TEST_F(MAYBE_PasswordFormConversionUtilsTest
, IdentifyingThreePasswordFields
) {
341 // Each test case consists of a set of parameters to be plugged into the
342 // PasswordFormBuilder below, plus the corresponding expectations.
344 const char* password_values
[3];
345 const char* expected_password_element
;
346 const char* expected_password_value
;
347 const char* expected_new_password_element
;
348 const char* expected_new_password_value
;
350 // Two fields with the same value, and one different: we should treat this
351 // as a password change form with confirmation for the new password. Note
352 // that we only recognize (current + new + new) and (new + new + current)
353 // without autocomplete attributes.
354 {{"alpha", "", ""}, "password1", "alpha", "password2", ""},
355 {{"", "beta", "beta"}, "password1", "", "password2", "beta"},
356 {{"alpha", "beta", "beta"}, "password1", "alpha", "password2", "beta"},
357 // If confirmed password comes first, assume that the third password
358 // field is related to security question, SSN, or credit card and ignore
360 {{"beta", "beta", "alpha"}, "", "", "password1", "beta"},
361 // If the fields are yet empty, we speculate that we will identify them as
362 // (current + new + new) once they are filled out, so we should classify
363 // them the same for now to keep our abstract interpretation less flaky.
364 {{"", "", ""}, "password1", "", "password2", ""}};
365 // Note: In all other cases, we give up and consider the form invalid.
366 // This is tested in InvalidFormDueToConfusingPasswordFields.
368 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
369 SCOPED_TRACE(testing::Message() << "Iteration " << i
);
371 PasswordFormBuilder
builder(kTestFormActionURL
);
372 builder
.AddPasswordField("password1", cases
[i
].password_values
[0], NULL
);
373 builder
.AddUsernameField("username1", "William", NULL
);
374 builder
.AddPasswordField("password2", cases
[i
].password_values
[1], NULL
);
375 builder
.AddUsernameField("username2", "Smith", NULL
);
376 builder
.AddPasswordField("password3", cases
[i
].password_values
[2], NULL
);
377 builder
.AddSubmitButton("submit", true);
378 std::string html
= builder
.ProduceHTML();
380 scoped_ptr
<PasswordForm
> password_form
;
381 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
382 ASSERT_TRUE(password_form
);
384 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_password_element
),
385 password_form
->password_element
);
386 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_password_value
),
387 password_form
->password_value
);
388 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_new_password_element
),
389 password_form
->new_password_element
);
390 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_new_password_value
),
391 password_form
->new_password_value
);
393 // Do a basic sanity check that we are still selecting the right username.
394 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form
->username_element
);
395 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form
->username_value
);
396 EXPECT_THAT(password_form
->other_possible_usernames
,
397 testing::ElementsAre(base::UTF8ToUTF16("Smith")));
401 TEST_F(MAYBE_PasswordFormConversionUtilsTest
,
402 IdentifyingPasswordFieldsWithAutocompleteAttributes
) {
403 // Each test case consists of a set of parameters to be plugged into the
404 // PasswordFormBuilder below, plus the corresponding expectations.
406 const char* autocomplete
[3];
407 const char* expected_password_element
;
408 const char* expected_password_value
;
409 const char* expected_new_password_element
;
410 const char* expected_new_password_value
;
411 bool expected_new_password_marked_by_site
;
413 // When there are elements marked with autocomplete='current-password',
414 // but no elements with 'new-password', we should treat the first of the
415 // former kind as the current password, and ignore all other password
416 // fields, assuming they are not intentionally not marked. They might be
417 // for other purposes, such as PINs, OTPs, and the like. Actual values in
418 // the password fields should be ignored in all cases below.
419 {{"current-password", NULL
, NULL
},
420 "password1", "alpha", "", "", false},
421 {{NULL
, "current-password", NULL
},
422 "password2", "beta", "", "", false},
423 {{NULL
, NULL
, "current-password"},
424 "password3", "gamma", "", "", false},
425 {{NULL
, "current-password", "current-password"},
426 "password2", "beta", "", "", false},
427 {{"current-password", NULL
, "current-password"},
428 "password1", "alpha", "", "", false},
429 {{"current-password", "current-password", NULL
},
430 "password1", "alpha", "", "", false},
431 {{"current-password", "current-password", "current-password"},
432 "password1", "alpha", "", "", false},
433 // The same goes vice versa for autocomplete='new-password'.
434 {{"new-password", NULL
, NULL
},
435 "", "", "password1", "alpha", true},
436 {{NULL
, "new-password", NULL
},
437 "", "", "password2", "beta", true},
438 {{NULL
, NULL
, "new-password"},
439 "", "", "password3", "gamma", true},
440 {{NULL
, "new-password", "new-password"},
441 "", "", "password2", "beta", true},
442 {{"new-password", NULL
, "new-password"},
443 "", "", "password1", "alpha", true},
444 {{"new-password", "new-password", NULL
},
445 "", "", "password1", "alpha", true},
446 {{"new-password", "new-password", "new-password"},
447 "", "", "password1", "alpha", true},
448 // When there is one element marked with autocomplete='current-password',
449 // and one with 'new-password', just comply, regardless of their order.
450 // Ignore the unmarked password field(s) for the same reason as above.
451 {{"current-password", "new-password", NULL
},
452 "password1", "alpha", "password2", "beta", true},
453 {{"current-password", NULL
, "new-password"},
454 "password1", "alpha", "password3", "gamma", true},
455 {{NULL
, "current-password", "new-password"},
456 "password2", "beta", "password3", "gamma", true},
457 {{"new-password", "current-password", NULL
},
458 "password2", "beta", "password1", "alpha", true},
459 {{"new-password", NULL
, "current-password"},
460 "password3", "gamma", "password1", "alpha", true},
461 {{NULL
, "new-password", "current-password"},
462 "password3", "gamma", "password2", "beta", true},
463 // In case of duplicated elements of either kind, go with the first one of
465 {{"current-password", "current-password", "new-password"},
466 "password1", "alpha", "password3", "gamma", true},
467 {{"current-password", "new-password", "current-password"},
468 "password1", "alpha", "password2", "beta", true},
469 {{"new-password", "current-password", "current-password"},
470 "password2", "beta", "password1", "alpha", true},
471 {{"current-password", "new-password", "new-password"},
472 "password1", "alpha", "password2", "beta", true},
473 {{"new-password", "current-password", "new-password"},
474 "password2", "beta", "password1", "alpha", true},
475 {{"new-password", "new-password", "current-password"},
476 "password3", "gamma", "password1", "alpha", true},
477 // When there is an empty autocomplete attribute (i.e. autocomplete=""),
478 // it should have the same effect as having no attribute whatsoever.
479 {{"current-password", "", ""},
480 "password1", "alpha", "", "", false},
481 {{"", "", "new-password"},
482 "", "", "password3", "gamma", true},
483 {{"", "new-password", ""},
484 "", "", "password2", "beta", true},
485 {{"", "current-password", "current-password"},
486 "password2", "beta", "", "", false},
487 {{"new-password", "", "new-password"},
488 "", "", "password1", "alpha", true},
489 {{"new-password", "", "current-password"},
490 "password3", "gamma", "password1", "alpha", true},
491 // It should not matter if attribute values are upper or mixed case.
492 {{NULL
, "current-password", NULL
},
493 "password2", "beta", "", "", false},
494 {{NULL
, "CURRENT-PASSWORD", NULL
},
495 "password2", "beta", "", "", false},
496 {{NULL
, "new-password", NULL
},
497 "", "", "password2", "beta", true},
498 {{NULL
, "nEw-PaSsWoRd", NULL
},
499 "", "", "password2", "beta", true}};
501 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
502 SCOPED_TRACE(testing::Message() << "Iteration " << i
);
504 PasswordFormBuilder
builder(kTestFormActionURL
);
505 builder
.AddPasswordField("pin1", "123456", NULL
);
506 builder
.AddPasswordField("pin2", "789101", NULL
);
507 builder
.AddPasswordField("password1", "alpha", cases
[i
].autocomplete
[0]);
508 builder
.AddUsernameField("username1", "William", NULL
);
509 builder
.AddPasswordField("password2", "beta", cases
[i
].autocomplete
[1]);
510 builder
.AddUsernameField("username2", "Smith", NULL
);
511 builder
.AddPasswordField("password3", "gamma", cases
[i
].autocomplete
[2]);
512 builder
.AddSubmitButton("submit", true);
513 std::string html
= builder
.ProduceHTML();
515 scoped_ptr
<PasswordForm
> password_form
;
516 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
517 ASSERT_TRUE(password_form
);
519 // In the absence of username autocomplete attributes, the username should
520 // be the text input field before the first password element.
521 // No constellation of password autocomplete attributes should change that.
522 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form
->username_element
);
523 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form
->username_value
);
524 EXPECT_THAT(password_form
->other_possible_usernames
,
525 testing::ElementsAre(base::UTF8ToUTF16("Smith")));
526 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_password_element
),
527 password_form
->password_element
);
528 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_password_value
),
529 password_form
->password_value
);
530 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_new_password_element
),
531 password_form
->new_password_element
);
532 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_new_password_value
),
533 password_form
->new_password_value
);
534 EXPECT_EQ(cases
[i
].expected_new_password_marked_by_site
,
535 password_form
->new_password_marked_by_site
);
539 TEST_F(MAYBE_PasswordFormConversionUtilsTest
, InvalidFormDueToBadActionURL
) {
540 PasswordFormBuilder
builder("invalid_target");
541 builder
.AddUsernameField("username", "JohnSmith", NULL
);
542 builder
.AddSubmitButton("submit", true);
543 builder
.AddPasswordField("password", "secret", NULL
);
544 std::string html
= builder
.ProduceHTML();
546 scoped_ptr
<PasswordForm
> password_form
;
547 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
548 EXPECT_FALSE(password_form
);
551 TEST_F(MAYBE_PasswordFormConversionUtilsTest
,
552 InvalidFormDueToNoPasswordFields
) {
553 PasswordFormBuilder
builder(kTestFormActionURL
);
554 builder
.AddUsernameField("username1", "John", NULL
);
555 builder
.AddUsernameField("username2", "Smith", NULL
);
556 builder
.AddSubmitButton("submit", true);
557 std::string html
= builder
.ProduceHTML();
559 scoped_ptr
<PasswordForm
> password_form
;
560 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
561 EXPECT_FALSE(password_form
);
564 TEST_F(MAYBE_PasswordFormConversionUtilsTest
,
565 InvalidFormsDueToConfusingPasswordFields
) {
566 // Each test case consists of a set of parameters to be plugged into the
567 // PasswordFormBuilder below.
568 const char* cases
[][3] = {
569 // No autocomplete attributes to guide us, and we see:
570 // * three password values that are all different,
571 // * three password values that are all the same;
572 // * three password values with the first and last matching.
573 // In any case, we should just give up on this form.
574 {"alpha", "beta", "gamma"},
575 {"alpha", "alpha", "alpha"},
576 {"alpha", "beta", "alpha"}};
578 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
579 SCOPED_TRACE(testing::Message() << "Iteration " << i
);
581 PasswordFormBuilder
builder(kTestFormActionURL
);
582 builder
.AddUsernameField("username1", "John", NULL
);
583 builder
.AddPasswordField("password1", cases
[i
][0], NULL
);
584 builder
.AddPasswordField("password2", cases
[i
][1], NULL
);
585 builder
.AddPasswordField("password3", cases
[i
][2], NULL
);
586 builder
.AddSubmitButton("submit", true);
587 std::string html
= builder
.ProduceHTML();
589 scoped_ptr
<PasswordForm
> password_form
;
590 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
591 EXPECT_FALSE(password_form
);
595 TEST_F(MAYBE_PasswordFormConversionUtilsTest
,
596 InvalidFormDueToTooManyPasswordFieldsWithoutAutocompleteAttributes
) {
597 PasswordFormBuilder
builder(kTestFormActionURL
);
598 builder
.AddUsernameField("username1", "John", NULL
);
599 builder
.AddPasswordField("password1", "alpha", NULL
);
600 builder
.AddPasswordField("password2", "alpha", NULL
);
601 builder
.AddPasswordField("password3", "alpha", NULL
);
602 builder
.AddPasswordField("password4", "alpha", NULL
);
603 builder
.AddSubmitButton("submit", true);
604 std::string html
= builder
.ProduceHTML();
606 scoped_ptr
<PasswordForm
> password_form
;
607 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
608 EXPECT_FALSE(password_form
);
611 TEST_F(MAYBE_PasswordFormConversionUtilsTest
, LayoutClassificationLogin
) {
612 PasswordFormBuilder
builder(kTestFormActionURL
);
613 builder
.AddHiddenField();
614 builder
.AddUsernameField("username", "", nullptr);
615 builder
.AddPasswordField("password", "", nullptr);
616 builder
.AddSubmitButton("submit", false);
617 std::string login_html
= builder
.ProduceHTML();
619 scoped_ptr
<PasswordForm
> login_form
;
620 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(login_html
, &login_form
));
621 ASSERT_TRUE(login_form
);
622 EXPECT_EQ(PasswordForm::Layout::LAYOUT_OTHER
, login_form
->layout
);
625 TEST_F(MAYBE_PasswordFormConversionUtilsTest
, LayoutClassificationSignup
) {
626 PasswordFormBuilder
builder(kTestFormActionURL
);
627 builder
.AddUsernameField("someotherfield", "", nullptr);
628 builder
.AddUsernameField("username", "", nullptr);
629 builder
.AddPasswordField("new_password", "", nullptr);
630 builder
.AddHiddenField();
631 builder
.AddPasswordField("new_password2", "", nullptr);
632 builder
.AddSubmitButton("submit", false);
633 std::string signup_html
= builder
.ProduceHTML();
635 scoped_ptr
<PasswordForm
> signup_form
;
636 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(signup_html
, &signup_form
));
637 ASSERT_TRUE(signup_form
);
638 EXPECT_EQ(PasswordForm::Layout::LAYOUT_OTHER
, signup_form
->layout
);
641 TEST_F(MAYBE_PasswordFormConversionUtilsTest
, LayoutClassificationChange
) {
642 PasswordFormBuilder
builder(kTestFormActionURL
);
643 builder
.AddUsernameField("username", "", nullptr);
644 builder
.AddPasswordField("old_password", "", nullptr);
645 builder
.AddHiddenField();
646 builder
.AddPasswordField("new_password", "", nullptr);
647 builder
.AddPasswordField("new_password2", "", nullptr);
648 builder
.AddSubmitButton("submit", false);
649 std::string change_html
= builder
.ProduceHTML();
651 scoped_ptr
<PasswordForm
> change_form
;
652 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(change_html
, &change_form
));
653 ASSERT_TRUE(change_form
);
654 EXPECT_EQ(PasswordForm::Layout::LAYOUT_OTHER
, change_form
->layout
);
657 TEST_F(MAYBE_PasswordFormConversionUtilsTest
,
658 LayoutClassificationLoginPlusSignup_A
) {
659 PasswordFormBuilder
builder(kTestFormActionURL
);
660 builder
.AddUsernameField("username", "", nullptr);
661 builder
.AddHiddenField();
662 builder
.AddPasswordField("password", "", nullptr);
663 builder
.AddUsernameField("username2", "", nullptr);
664 builder
.AddUsernameField("someotherfield", "", nullptr);
665 builder
.AddPasswordField("new_password", "", nullptr);
666 builder
.AddPasswordField("new_password2", "", nullptr);
667 builder
.AddHiddenField();
668 builder
.AddSubmitButton("submit", false);
669 std::string login_plus_signup_html
= builder
.ProduceHTML();
671 scoped_ptr
<PasswordForm
> login_plus_signup_form
;
672 ASSERT_NO_FATAL_FAILURE(
673 LoadHTMLAndConvertForm(login_plus_signup_html
, &login_plus_signup_form
));
674 ASSERT_TRUE(login_plus_signup_form
);
675 EXPECT_EQ(PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP
,
676 login_plus_signup_form
->layout
);
679 TEST_F(MAYBE_PasswordFormConversionUtilsTest
,
680 LayoutClassificationLoginPlusSignup_B
) {
681 PasswordFormBuilder
builder(kTestFormActionURL
);
682 builder
.AddUsernameField("username", "", nullptr);
683 builder
.AddHiddenField();
684 builder
.AddPasswordField("password", "", nullptr);
685 builder
.AddUsernameField("username2", "", nullptr);
686 builder
.AddUsernameField("someotherfield", "", nullptr);
687 builder
.AddPasswordField("new_password", "", nullptr);
688 builder
.AddUsernameField("someotherfield2", "", nullptr);
689 builder
.AddHiddenField();
690 builder
.AddSubmitButton("submit", false);
691 std::string login_plus_signup_html
= builder
.ProduceHTML();
693 scoped_ptr
<PasswordForm
> login_plus_signup_form
;
694 ASSERT_NO_FATAL_FAILURE(
695 LoadHTMLAndConvertForm(login_plus_signup_html
, &login_plus_signup_form
));
696 ASSERT_TRUE(login_plus_signup_form
);
697 EXPECT_EQ(PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP
,
698 login_plus_signup_form
->layout
);
701 } // namespace autofill