OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/strings/string16.h" | 5 #include "base/strings/string16.h" |
6 #include "base/strings/string_util.h" | 6 #include "base/strings/string_util.h" |
7 #include "base/strings/stringprintf.h" | 7 #include "base/strings/stringprintf.h" |
8 #include "base/strings/utf_string_conversions.h" | 8 #include "base/strings/utf_string_conversions.h" |
9 #include "components/autofill/content/renderer/password_form_conversion_utils.h" | 9 #include "components/autofill/content/renderer/password_form_conversion_utils.h" |
10 #include "components/autofill/core/common/password_form.h" | 10 #include "components/autofill/core/common/password_form.h" |
11 #include "content/public/test/render_view_test.h" | 11 #include "content/public/test/render_view_test.h" |
| 12 #include "testing/gmock/include/gmock/gmock.h" |
12 #include "testing/gtest/include/gtest/gtest.h" | 13 #include "testing/gtest/include/gtest/gtest.h" |
13 #include "third_party/WebKit/public/platform/WebVector.h" | 14 #include "third_party/WebKit/public/platform/WebVector.h" |
14 #include "third_party/WebKit/public/web/WebDocument.h" | 15 #include "third_party/WebKit/public/web/WebDocument.h" |
| 16 #include "third_party/WebKit/public/web/WebFormControlElement.h" |
15 #include "third_party/WebKit/public/web/WebFormElement.h" | 17 #include "third_party/WebKit/public/web/WebFormElement.h" |
| 18 #include "third_party/WebKit/public/web/WebInputElement.h" |
16 #include "third_party/WebKit/public/web/WebLocalFrame.h" | 19 #include "third_party/WebKit/public/web/WebLocalFrame.h" |
17 | 20 |
| 21 using blink::WebFormControlElement; |
18 using blink::WebFormElement; | 22 using blink::WebFormElement; |
19 using blink::WebFrame; | 23 using blink::WebFrame; |
| 24 using blink::WebInputElement; |
20 using blink::WebVector; | 25 using blink::WebVector; |
21 | 26 |
22 namespace autofill { | 27 namespace autofill { |
23 namespace { | 28 namespace { |
24 | 29 |
25 const char kTestFormActionURL[] = "http://cnn.com"; | 30 const char kTestFormActionURL[] = "http://cnn.com"; |
26 | 31 |
27 // A builder to produce HTML code for a password form composed of the desired | 32 // A builder to produce HTML code for a password form composed of the desired |
28 // number and kinds of username and password fields. | 33 // number and kinds of username and password fields. |
29 class PasswordFormBuilder { | 34 class PasswordFormBuilder { |
(...skipping 28 matching lines...) Expand all Loading... |
58 const char* value, | 63 const char* value, |
59 const char* autocomplete) { | 64 const char* autocomplete) { |
60 std::string autocomplete_attribute(autocomplete ? | 65 std::string autocomplete_attribute(autocomplete ? |
61 base::StringPrintf("autocomplete=\"%s\"", autocomplete): ""); | 66 base::StringPrintf("autocomplete=\"%s\"", autocomplete): ""); |
62 base::StringAppendF( | 67 base::StringAppendF( |
63 &html_, | 68 &html_, |
64 "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>", | 69 "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>", |
65 name_and_id, name_and_id, value, autocomplete_attribute.c_str()); | 70 name_and_id, name_and_id, value, autocomplete_attribute.c_str()); |
66 } | 71 } |
67 | 72 |
68 // Appends a new submit-type field at the end of the form. | 73 // Appends a disabled text-type field at the end of the form. |
69 void AddSubmitButton() { | 74 void AddDisabledUsernameField() { |
70 html_ += "<INPUT type=\"submit\" name=\"submit\" value=\"Submit\"/>"; | 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" : ""); |
71 } | 91 } |
72 | 92 |
73 // Returns the HTML code for the form containing the fields that have been | 93 // Returns the HTML code for the form containing the fields that have been |
74 // added so far. | 94 // added so far. |
75 std::string ProduceHTML() const { | 95 std::string ProduceHTML() const { |
76 return html_ + "</FORM>"; | 96 return html_ + "</FORM>"; |
77 } | 97 } |
78 | 98 |
79 private: | 99 private: |
80 std::string html_; | 100 std::string html_; |
(...skipping 15 matching lines...) Expand all Loading... |
96 scoped_ptr<PasswordForm>* password_form) { | 116 scoped_ptr<PasswordForm>* password_form) { |
97 LoadHTML(html.c_str()); | 117 LoadHTML(html.c_str()); |
98 | 118 |
99 WebFrame* frame = GetMainFrame(); | 119 WebFrame* frame = GetMainFrame(); |
100 ASSERT_NE(static_cast<WebFrame*>(NULL), frame); | 120 ASSERT_NE(static_cast<WebFrame*>(NULL), frame); |
101 | 121 |
102 WebVector<WebFormElement> forms; | 122 WebVector<WebFormElement> forms; |
103 frame->document().forms(forms); | 123 frame->document().forms(forms); |
104 ASSERT_EQ(1U, forms.size()); | 124 ASSERT_EQ(1U, forms.size()); |
105 | 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 |
106 *password_form = CreatePasswordForm(forms[0]); | 134 *password_form = CreatePasswordForm(forms[0]); |
107 } | 135 } |
108 | 136 |
109 private: | 137 private: |
110 DISALLOW_COPY_AND_ASSIGN(PasswordFormConversionUtilsTest); | 138 DISALLOW_COPY_AND_ASSIGN(PasswordFormConversionUtilsTest); |
111 }; | 139 }; |
112 | 140 |
113 } // namespace | 141 } // namespace |
114 | 142 |
115 TEST_F(PasswordFormConversionUtilsTest, ValidWebFormElementToPasswordForm) { | 143 TEST_F(PasswordFormConversionUtilsTest, BasicFormAttributes) { |
116 PasswordFormBuilder builder(kTestFormActionURL); | 144 PasswordFormBuilder builder(kTestFormActionURL); |
117 builder.AddUsernameField("username", "johnsmith", NULL); | 145 builder.AddUsernameField("username", "johnsmith", NULL); |
118 builder.AddSubmitButton(); | 146 builder.AddSubmitButton("inactive_submit", false); |
| 147 builder.AddSubmitButton("active_submit", true); |
| 148 builder.AddSubmitButton("inactive_submit2", false); |
119 builder.AddPasswordField("password", "secret", NULL); | 149 builder.AddPasswordField("password", "secret", NULL); |
120 std::string html = builder.ProduceHTML(); | 150 std::string html = builder.ProduceHTML(); |
121 | 151 |
122 scoped_ptr<PasswordForm> password_form; | 152 scoped_ptr<PasswordForm> password_form; |
123 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); | 153 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
124 ASSERT_NE(static_cast<PasswordForm*>(NULL), password_form.get()); | 154 ASSERT_TRUE(password_form); |
125 | 155 |
126 EXPECT_EQ("data:", password_form->signon_realm); | 156 EXPECT_EQ("data:", password_form->signon_realm); |
127 EXPECT_EQ(GURL(kTestFormActionURL), password_form->action); | 157 EXPECT_EQ(GURL(kTestFormActionURL), password_form->action); |
| 158 EXPECT_EQ(base::UTF8ToUTF16("active_submit"), password_form->submit_element); |
128 EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); | 159 EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element); |
129 EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); | 160 EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value); |
130 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); | 161 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); |
131 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); | 162 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); |
132 EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form->scheme); | 163 EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form->scheme); |
133 EXPECT_FALSE(password_form->ssl_valid); | 164 EXPECT_FALSE(password_form->ssl_valid); |
134 EXPECT_FALSE(password_form->preferred); | 165 EXPECT_FALSE(password_form->preferred); |
135 EXPECT_FALSE(password_form->blacklisted_by_user); | 166 EXPECT_FALSE(password_form->blacklisted_by_user); |
136 EXPECT_EQ(PasswordForm::TYPE_MANUAL, password_form->type); | 167 EXPECT_EQ(PasswordForm::TYPE_MANUAL, password_form->type); |
| 168 EXPECT_FALSE(password_form->use_additional_authentication); |
137 } | 169 } |
138 | 170 |
139 TEST_F(PasswordFormConversionUtilsTest, InvalidWebFormElementToPasswordForm) { | 171 TEST_F(PasswordFormConversionUtilsTest, DisabledFieldsAreIgnored) { |
140 PasswordFormBuilder builder("invalid_target"); | 172 PasswordFormBuilder builder(kTestFormActionURL); |
141 builder.AddUsernameField("username", "johnsmith", NULL); | 173 builder.AddUsernameField("username", "johnsmith", NULL); |
142 builder.AddSubmitButton(); | 174 builder.AddDisabledUsernameField(); |
| 175 builder.AddDisabledPasswordField(); |
143 builder.AddPasswordField("password", "secret", NULL); | 176 builder.AddPasswordField("password", "secret", NULL); |
| 177 builder.AddSubmitButton("submit", true); |
144 std::string html = builder.ProduceHTML(); | 178 std::string html = builder.ProduceHTML(); |
145 | 179 |
146 scoped_ptr<PasswordForm> password_form; | 180 scoped_ptr<PasswordForm> password_form; |
147 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); | 181 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
148 ASSERT_EQ(static_cast<PasswordForm*>(NULL), password_form.get()); | 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); |
149 } | 187 } |
150 | 188 |
151 TEST_F(PasswordFormConversionUtilsTest, | 189 TEST_F(PasswordFormConversionUtilsTest, IdentifyingUsernameFields) { |
152 WebFormWithMultipleUseNameAndPassWordFieldsToPasswordForm) { | |
153 PasswordFormBuilder builder(kTestFormActionURL); | |
154 builder.AddUsernameField("username1", "John", NULL); | |
155 builder.AddPasswordField("password1", "oldsecret", NULL); | |
156 builder.AddUsernameField("username2", "William", NULL); | |
157 builder.AddPasswordField("password2", "secret", NULL); | |
158 builder.AddUsernameField("username3", "Smith", NULL); | |
159 builder.AddPasswordField("password3", "secret", NULL); | |
160 builder.AddSubmitButton(); | |
161 std::string html = builder.ProduceHTML(); | |
162 | |
163 scoped_ptr<PasswordForm> password_form; | |
164 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); | |
165 ASSERT_NE(static_cast<PasswordForm*>(NULL), password_form.get()); | |
166 | |
167 EXPECT_EQ("data:", password_form->signon_realm); | |
168 EXPECT_EQ(GURL(kTestFormActionURL), password_form->action); | |
169 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element); | |
170 EXPECT_EQ(base::UTF8ToUTF16("John"), password_form->username_value); | |
171 EXPECT_EQ(base::UTF8ToUTF16("password1"), | |
172 password_form->password_element); | |
173 EXPECT_EQ(base::UTF8ToUTF16("oldsecret"), password_form->password_value); | |
174 EXPECT_EQ(base::UTF8ToUTF16("password2"), | |
175 password_form->new_password_element); | |
176 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->new_password_value); | |
177 ASSERT_EQ(2u, password_form->other_possible_usernames.size()); | |
178 EXPECT_EQ(base::UTF8ToUTF16("William"), | |
179 password_form->other_possible_usernames[0]); | |
180 EXPECT_EQ(base::UTF8ToUTF16("Smith"), | |
181 password_form->other_possible_usernames[1]); | |
182 EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form->scheme); | |
183 EXPECT_FALSE(password_form->ssl_valid); | |
184 EXPECT_FALSE(password_form->preferred); | |
185 EXPECT_FALSE(password_form->blacklisted_by_user); | |
186 EXPECT_EQ(PasswordForm::TYPE_MANUAL, password_form->type); | |
187 } | |
188 | |
189 TEST_F(PasswordFormConversionUtilsTest, | |
190 WebFormwithThreeDifferentPasswordsToPasswordForm) { | |
191 PasswordFormBuilder builder(kTestFormActionURL); | |
192 builder.AddUsernameField("username1", "John", NULL); | |
193 builder.AddPasswordField("password1", "alpha", NULL); | |
194 builder.AddPasswordField("password2", "beta", NULL); | |
195 builder.AddPasswordField("password3", "gamma", NULL); | |
196 builder.AddSubmitButton(); | |
197 std::string html = builder.ProduceHTML(); | |
198 | |
199 scoped_ptr<PasswordForm> password_form; | |
200 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); | |
201 ASSERT_EQ(static_cast<PasswordForm*>(NULL), password_form.get()); | |
202 } | |
203 | |
204 TEST_F(PasswordFormConversionUtilsTest, | |
205 UsernameFieldsWithAutocompleteAttributes) { | |
206 // Each test case consists of a set of parameters to be plugged into the | 190 // Each test case consists of a set of parameters to be plugged into the |
207 // PasswordFormBuilder below, plus the corresponding expectations. | 191 // PasswordFormBuilder below, plus the corresponding expectations. |
208 struct TestCase { | 192 struct TestCase { |
209 const char* autocomplete[3]; | 193 const char* autocomplete[3]; |
210 const char* expected_username_element; | 194 const char* expected_username_element; |
211 const char* expected_username_value; | 195 const char* expected_username_value; |
212 const char* expected_other_possible_usernames; | 196 const char* expected_other_possible_usernames; |
213 } cases[] = { | 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"}, |
214 // When a sole element is marked with autocomplete='username', it should | 202 // When a sole element is marked with autocomplete='username', it should |
215 // be treated as the username for sure, with no other_possible_usernames. | 203 // be treated as the username for sure, with no other_possible_usernames. |
216 {{"username", NULL, NULL}, "username1", "John", ""}, | 204 {{"username", NULL, NULL}, "username1", "John", ""}, |
217 {{NULL, "username", NULL}, "username2", "William", ""}, | 205 {{NULL, "username", NULL}, "username2", "William", ""}, |
218 {{NULL, NULL, "username"}, "username3", "Smith", ""}, | 206 {{NULL, NULL, "username"}, "username3", "Smith", ""}, |
219 // When >=2 elements have the attribute, the first should be selected as | 207 // When >=2 elements have the attribute, the first should be selected as |
220 // the username, and the rest should go to other_possible_usernames. | 208 // the username, and the rest should go to other_possible_usernames. |
221 {{"username", "username", NULL}, "username1", "John", "William"}, | 209 {{"username", "username", NULL}, "username1", "John", "William"}, |
222 {{NULL, "username", "username"}, "username2", "William", "Smith"}, | 210 {{NULL, "username", "username"}, "username2", "William", "Smith"}, |
223 {{"username", NULL, "username"}, "username1", "John", "Smith"}, | 211 {{"username", NULL, "username"}, "username1", "John", "Smith"}, |
224 {{"username", "username", "username"}, "username1", "John", | 212 {{"username", "username", "username"}, "username1", "John", |
225 "William+Smith"}, | 213 "William+Smith"}, |
226 // When there is an empty autocomplete attribute (i.e. autocomplete=""), | 214 // When there is an empty autocomplete attribute (i.e. autocomplete=""), |
227 // it should have the same effect as having no attribute whatsoever. | 215 // it should have the same effect as having no attribute whatsoever. |
228 {{"", "", ""}, "username2", "William", "John+Smith"}, | 216 {{"", "", ""}, "username2", "William", "John+Smith"}, |
229 {{"", "", "username"}, "username3", "Smith", ""}, | 217 {{"", "", "username"}, "username3", "Smith", ""}, |
230 {{"username", "", "username"}, "username1", "John", "Smith"}, | 218 {{"username", "", "username"}, "username1", "John", "Smith"}, |
231 // It should not matter if attribute values are upper or mixed case. | 219 // It should not matter if attribute values are upper or mixed case. |
232 {{"USERNAME", NULL, "uSeRNaMe"}, "username1", "John", "Smith"}, | 220 {{"USERNAME", NULL, "uSeRNaMe"}, "username1", "John", "Smith"}, |
233 {{"uSeRNaMe", NULL, "USERNAME"}, "username1", "John", "Smith"}}; | 221 {{"uSeRNaMe", NULL, "USERNAME"}, "username1", "John", "Smith"}}; |
234 | 222 |
235 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { | 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 |
| 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. |
| 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) { |
236 SCOPED_TRACE(testing::Message() << "Iteration " << i); | 298 SCOPED_TRACE(testing::Message() << "Iteration " << i); |
237 | 299 |
238 PasswordFormBuilder builder(kTestFormActionURL); | 300 PasswordFormBuilder builder(kTestFormActionURL); |
239 builder.AddUsernameField("username1", "John", cases[i].autocomplete[0]); | 301 builder.AddPasswordField("password1", cases[i].password_values[0], NULL); |
240 builder.AddUsernameField("username2", "William", cases[i].autocomplete[1]); | 302 builder.AddUsernameField("username1", "William", NULL); |
241 builder.AddPasswordField("password", "secret", NULL); | 303 builder.AddPasswordField("password2", cases[i].password_values[1], NULL); |
242 builder.AddUsernameField("username3", "Smith", cases[i].autocomplete[2]); | 304 builder.AddUsernameField("username2", "Smith", NULL); |
243 builder.AddSubmitButton(); | 305 builder.AddSubmitButton("submit", true); |
244 std::string html = builder.ProduceHTML(); | 306 std::string html = builder.ProduceHTML(); |
245 | 307 |
246 scoped_ptr<PasswordForm> password_form; | 308 scoped_ptr<PasswordForm> password_form; |
247 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); | 309 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
248 ASSERT_NE(static_cast<PasswordForm*>(NULL), password_form.get()); | 310 ASSERT_TRUE(password_form); |
249 | 311 |
250 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element), | 312 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element), |
251 password_form->username_element); | 313 password_form->password_element); |
252 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value), | 314 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value), |
253 password_form->username_value); | 315 password_form->password_value); |
254 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element); | 316 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element), |
255 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value); | 317 password_form->new_password_element); |
256 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_other_possible_usernames), | 318 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value), |
257 JoinString(password_form->other_possible_usernames, '+')); | 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 |
| 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. |
| 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"))); |
258 } | 384 } |
259 } | 385 } |
260 | 386 |
261 TEST_F(PasswordFormConversionUtilsTest, | 387 TEST_F(PasswordFormConversionUtilsTest, |
262 PasswordFieldsWithAutocompleteAttributes) { | 388 IdentifyingPasswordFieldsWithAutocompleteAttributes) { |
263 // Each test case consists of a set of parameters to be plugged into the | 389 // Each test case consists of a set of parameters to be plugged into the |
264 // PasswordFormBuilder below, plus the corresponding expectations. | 390 // PasswordFormBuilder below, plus the corresponding expectations. |
265 struct TestCase { | 391 struct TestCase { |
266 const char* autocomplete[3]; | 392 const char* autocomplete[3]; |
267 const char* expected_password_element; | 393 const char* expected_password_element; |
268 const char* expected_password_value; | 394 const char* expected_password_value; |
269 const char* expected_new_password_element; | 395 const char* expected_new_password_element; |
270 const char* expected_new_password_value; | 396 const char* expected_new_password_value; |
271 } cases[] = { | 397 } cases[] = { |
272 // When there are elements marked with autocomplete='current-password', | 398 // When there are elements marked with autocomplete='current-password', |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
354 "password2", "beta", "", ""}, | 480 "password2", "beta", "", ""}, |
355 {{NULL, "new-password", NULL}, | 481 {{NULL, "new-password", NULL}, |
356 "", "", "password2", "beta"}, | 482 "", "", "password2", "beta"}, |
357 {{NULL, "nEw-PaSsWoRd", NULL}, | 483 {{NULL, "nEw-PaSsWoRd", NULL}, |
358 "", "", "password2", "beta"}}; | 484 "", "", "password2", "beta"}}; |
359 | 485 |
360 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { | 486 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { |
361 SCOPED_TRACE(testing::Message() << "Iteration " << i); | 487 SCOPED_TRACE(testing::Message() << "Iteration " << i); |
362 | 488 |
363 PasswordFormBuilder builder(kTestFormActionURL); | 489 PasswordFormBuilder builder(kTestFormActionURL); |
| 490 builder.AddPasswordField("pin1", "123456", NULL); |
| 491 builder.AddPasswordField("pin2", "789101", NULL); |
364 builder.AddPasswordField("password1", "alpha", cases[i].autocomplete[0]); | 492 builder.AddPasswordField("password1", "alpha", cases[i].autocomplete[0]); |
365 builder.AddUsernameField("username1", "William", NULL); | 493 builder.AddUsernameField("username1", "William", NULL); |
366 builder.AddPasswordField("password2", "beta", cases[i].autocomplete[1]); | 494 builder.AddPasswordField("password2", "beta", cases[i].autocomplete[1]); |
367 builder.AddUsernameField("username2", "Smith", NULL); | 495 builder.AddUsernameField("username2", "Smith", NULL); |
368 builder.AddPasswordField("password3", "gamma", cases[i].autocomplete[2]); | 496 builder.AddPasswordField("password3", "gamma", cases[i].autocomplete[2]); |
369 builder.AddSubmitButton(); | 497 builder.AddSubmitButton("submit", true); |
370 std::string html = builder.ProduceHTML(); | 498 std::string html = builder.ProduceHTML(); |
371 | 499 |
372 scoped_ptr<PasswordForm> password_form; | 500 scoped_ptr<PasswordForm> password_form; |
373 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); | 501 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
374 ASSERT_NE(static_cast<PasswordForm*>(NULL), password_form.get()); | 502 ASSERT_TRUE(password_form); |
375 | 503 |
376 // Any constellation of password autocomplete attributes should have no | 504 // In the absence of username autocomplete attributes, the username should |
377 // effect on that the first text-type input field before a password field | 505 // be the text input field before the first password element. |
378 // should be selected as the username. | 506 // No constellation of password autocomplete attributes should change that. |
379 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element); | 507 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element); |
380 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); | 508 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value); |
| 509 EXPECT_THAT(password_form->other_possible_usernames, |
| 510 testing::ElementsAre(base::UTF8ToUTF16("Smith"))); |
381 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element), | 511 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element), |
382 password_form->password_element); | 512 password_form->password_element); |
383 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value), | 513 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value), |
384 password_form->password_value); | 514 password_form->password_value); |
385 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element), | 515 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element), |
386 password_form->new_password_element); | 516 password_form->new_password_element); |
387 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value), | 517 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value), |
388 password_form->new_password_value); | 518 password_form->new_password_value); |
389 ASSERT_EQ(1u, password_form->other_possible_usernames.size()); | |
390 EXPECT_EQ(base::UTF8ToUTF16("Smith"), | |
391 password_form->other_possible_usernames[0]); | |
392 } | 519 } |
393 } | 520 } |
394 | 521 |
| 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(); |
| 528 |
| 529 scoped_ptr<PasswordForm> password_form; |
| 530 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 531 EXPECT_FALSE(password_form); |
| 532 } |
| 533 |
| 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(); |
| 540 |
| 541 scoped_ptr<PasswordForm> password_form; |
| 542 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 543 EXPECT_FALSE(password_form); |
| 544 } |
| 545 |
| 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"}}; |
| 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 |
| 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(); |
| 587 |
| 588 scoped_ptr<PasswordForm> password_form; |
| 589 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form)); |
| 590 EXPECT_FALSE(password_form); |
| 591 } |
| 592 |
395 } // namespace autofill | 593 } // namespace autofill |
OLD | NEW |