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 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
) {
89 "<INPUT type=\"submit\" name=\"%s\" value=\"Submit\" %s/>",
90 name
, activated
? "set-activated-submit" : "");
93 // Returns the HTML code for the form containing the fields that have been
95 std::string
ProduceHTML() const {
96 return html_
+ "</FORM>";
102 DISALLOW_COPY_AND_ASSIGN(PasswordFormBuilder
);
105 class PasswordFormConversionUtilsTest
: public content::RenderViewTest
{
107 PasswordFormConversionUtilsTest() : content::RenderViewTest() {}
108 virtual ~PasswordFormConversionUtilsTest() {}
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());
119 WebFrame
* frame
= GetMainFrame();
120 ASSERT_NE(static_cast<WebFrame
*>(NULL
), frame
);
122 WebVector
<WebFormElement
> forms
;
123 frame
->document().forms(forms
);
124 ASSERT_EQ(1U, forms
.size());
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);
134 *password_form
= CreatePasswordForm(forms
[0]);
138 DISALLOW_COPY_AND_ASSIGN(PasswordFormConversionUtilsTest
);
143 TEST_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();
152 scoped_ptr
<PasswordForm
> password_form
;
153 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
154 ASSERT_TRUE(password_form
);
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
);
171 TEST_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();
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
);
189 TEST_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.
193 const char* autocomplete
[3];
194 const char* expected_username_element
;
195 const char* expected_username_value
;
196 const char* expected_other_possible_usernames
;
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",
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"}};
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"));
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
) {
235 names
[1] = "William";
238 names
[0] = names
[1] = names
[2] = "";
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();
250 scoped_ptr
<PasswordForm
> password_form
;
251 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
252 ASSERT_TRUE(password_form
);
254 EXPECT_EQ(base::UTF8ToUTF16(cases
[i
].expected_username_element
),
255 password_form
->username_element
);
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
, '+'));
263 EXPECT_TRUE(password_form
->username_value
.empty());
264 EXPECT_TRUE(password_form
->other_possible_usernames
.empty());
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
);
274 TEST_F(PasswordFormConversionUtilsTest
, IdentifyingTwoPasswordFields
) {
275 // Each test case consists of a set of parameters to be plugged into the
276 // PasswordFormBuilder below, plus the corresponding expectations.
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
;
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"}};
297 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(cases
); ++i
) {
298 SCOPED_TRACE(testing::Message() << "Iteration " << i
);
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();
308 scoped_ptr
<PasswordForm
> password_form
;
309 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
310 ASSERT_TRUE(password_form
);
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
);
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")));
329 TEST_F(PasswordFormConversionUtilsTest
, IdentifyingThreePasswordFields
) {
330 // Each test case consists of a set of parameters to be plugged into the
331 // PasswordFormBuilder below, plus the corresponding expectations.
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
;
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.
354 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(cases
); ++i
) {
355 SCOPED_TRACE(testing::Message() << "Iteration " << i
);
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();
366 scoped_ptr
<PasswordForm
> password_form
;
367 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
368 ASSERT_TRUE(password_form
);
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
);
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")));
387 TEST_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.
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
;
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
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"}};
486 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(cases
); ++i
) {
487 SCOPED_TRACE(testing::Message() << "Iteration " << i
);
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();
500 scoped_ptr
<PasswordForm
> password_form
;
501 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
502 ASSERT_TRUE(password_form
);
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
);
522 TEST_F(PasswordFormConversionUtilsTest
, InvalidFormDueToBadActionURL
) {
523 PasswordFormBuilder
builder("invalid_target");
524 builder
.AddUsernameField("username", "JohnSmith", NULL
);
525 builder
.AddSubmitButton("submit", true);
526 builder
.AddPasswordField("password", "secret", NULL
);
527 std::string html
= builder
.ProduceHTML();
529 scoped_ptr
<PasswordForm
> password_form
;
530 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
531 EXPECT_FALSE(password_form
);
534 TEST_F(PasswordFormConversionUtilsTest
, InvalidFormDueToNoPasswordFields
) {
535 PasswordFormBuilder
builder(kTestFormActionURL
);
536 builder
.AddUsernameField("username1", "John", NULL
);
537 builder
.AddUsernameField("username2", "Smith", NULL
);
538 builder
.AddSubmitButton("submit", true);
539 std::string html
= builder
.ProduceHTML();
541 scoped_ptr
<PasswordForm
> password_form
;
542 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
543 EXPECT_FALSE(password_form
);
546 TEST_F(PasswordFormConversionUtilsTest
,
547 InvalidFormsDueToConfusingPasswordFields
) {
548 // Each test case consists of a set of parameters to be plugged into the
549 // PasswordFormBuilder below.
550 const char* cases
[][3] = {
551 // No autocomplete attributes to guide us, and we see:
552 // * three password values that are all different,
553 // * three password values that are all the same;
554 // * three password values with the first and last matching.
555 // In any case, we should just give up on this form.
556 {"alpha", "beta", "gamma"},
557 {"alpha", "alpha", "alpha"},
558 {"alpha", "beta", "alpha"}};
560 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(cases
); ++i
) {
561 SCOPED_TRACE(testing::Message() << "Iteration " << i
);
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();
571 scoped_ptr
<PasswordForm
> password_form
;
572 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
573 EXPECT_FALSE(password_form
);
577 TEST_F(PasswordFormConversionUtilsTest
,
578 InvalidFormDueToTooManyPasswordFieldsWithoutAutocompleteAttributes
) {
579 PasswordFormBuilder
builder(kTestFormActionURL
);
580 builder
.AddUsernameField("username1", "John", NULL
);
581 builder
.AddPasswordField("password1", "alpha", NULL
);
582 builder
.AddPasswordField("password2", "alpha", NULL
);
583 builder
.AddPasswordField("password3", "alpha", NULL
);
584 builder
.AddPasswordField("password4", "alpha", NULL
);
585 builder
.AddSubmitButton("submit", true);
586 std::string html
= builder
.ProduceHTML();
588 scoped_ptr
<PasswordForm
> password_form
;
589 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html
, &password_form
));
590 EXPECT_FALSE(password_form
);
593 } // namespace autofill