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