Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(591)

Side by Side Diff: chrome/renderer/autofill/password_autofill_manager_unittest.cc

Issue 7576001: Refactor webkit_glue::FormField to remove hacky methods (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix copyright header Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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/string_util.h"
6 #include "base/utf_string_conversions.h"
7 #include "chrome/common/autofill_messages.h"
8 #include "chrome/renderer/autofill/autofill_agent.h"
9 #include "chrome/renderer/autofill/password_autofill_manager.h"
10 #include "chrome/test/base/render_view_test.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormElement.h"
15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h"
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebVector.h"
19 #include "ui/base/keycodes/keyboard_codes.h"
20 #include "webkit/glue/form_data.h"
21 #include "webkit/glue/form_field.h"
22
23 using webkit_glue::FormField;
24 using webkit_glue::PasswordFormFillData;
25 using webkit_glue::PasswordForm;
26 using webkit_glue::PasswordFormDomManager;
27 using WebKit::WebDocument;
28 using WebKit::WebElement;
29 using WebKit::WebFrame;
30 using WebKit::WebInputElement;
31 using WebKit::WebString;
32
33 namespace {
34
35 // The name of the username/password element in the form.
36 const char* const kUsernameName = "username";
37 const char* const kPasswordName = "password";
38
39 const char* const kAliceUsername = "alice";
40 const char* const kAlicePassword = "password";
41 const char* const kBobUsername = "bob";
42 const char* const kBobPassword = "secret";
43 const char* const kCarolUsername = "Carol";
44 const char* const kCarolPassword = "test";
45
46
47 const char* const kFormHTML =
48 "<FORM name='LoginTestForm' action='http://www.bidule.com'>"
49 " <INPUT type='text' id='username'/>"
50 " <INPUT type='password' id='password'/>"
51 " <INPUT type='submit' value='Login'/>"
52 "</FORM>";
53
54 } // namespace
55
56 namespace autofill {
57
58 class PasswordAutofillManagerTest : public RenderViewTest {
59 public:
60 PasswordAutofillManagerTest() {
61 }
62
63 // Simulates the fill password form message being sent to the renderer.
64 // We use that so we don't have to make RenderView::OnFillPasswordForm()
65 // protected.
66 void SimulateOnFillPasswordForm(
67 const PasswordFormFillData& fill_data) {
68 AutofillMsg_FillPasswordForm msg(0, fill_data);
69 password_autofill_->OnMessageReceived(msg);
70 }
71
72 virtual void SetUp() {
73 RenderViewTest::SetUp();
74
75 // Add a preferred login and an additional login to the FillData.
76 username1_ = ASCIIToUTF16(kAliceUsername);
77 password1_ = ASCIIToUTF16(kAlicePassword);
78 username2_ = ASCIIToUTF16(kBobUsername);
79 password2_ = ASCIIToUTF16(kBobPassword);
80 username3_ = ASCIIToUTF16(kCarolUsername);
81 password3_ = ASCIIToUTF16(kCarolPassword);
82
83 fill_data_.basic_data.fields.push_back(
84 FormField(string16(), ASCIIToUTF16(kUsernameName),
85 username1_, string16(), 0, false));
86 fill_data_.basic_data.fields.push_back(
87 FormField(string16(), ASCIIToUTF16(kPasswordName),
88 password1_, string16(), 0, false));
89 fill_data_.additional_logins[username2_] = password2_;
90 fill_data_.additional_logins[username3_] = password3_;
91
92 // We need to set the origin so it matches the frame URL and the action so
93 // it matches the form action, otherwise we won't autocomplete.
94 std::string origin("data:text/html;charset=utf-8,");
95 origin += kFormHTML;
96 fill_data_.basic_data.origin = GURL(origin);
97 fill_data_.basic_data.action = GURL("http://www.bidule.com");
98
99 LoadHTML(kFormHTML);
100
101 // Now retrieves the input elements so the test can access them.
102 WebDocument document = GetMainFrame()->document();
103 WebElement element =
104 document.getElementById(WebString::fromUTF8(kUsernameName));
105 ASSERT_FALSE(element.isNull());
106 username_element_ = element.to<WebKit::WebInputElement>();
107 element = document.getElementById(WebString::fromUTF8(kPasswordName));
108 ASSERT_FALSE(element.isNull());
109 password_element_ = element.to<WebKit::WebInputElement>();
110 }
111
112 void ClearUsernameAndPasswordFields() {
113 username_element_.setValue("");
114 username_element_.setAutofilled(false);
115 password_element_.setValue("");
116 password_element_.setAutofilled(false);
117 }
118
119 void SimulateUsernameChange(const std::string& username,
120 bool move_caret_to_end) {
121 username_element_.setValue(WebString::fromUTF8(username));
122 if (move_caret_to_end)
123 username_element_.setSelectionRange(username.length(), username.length());
124 autofill_agent_->textFieldDidChange(username_element_);
125 // Processing is delayed because of a WebKit bug, see
126 // PasswordAutocompleteManager::TextDidChangeInTextField() for details.
127 MessageLoop::current()->RunAllPending();
128 }
129
130 void SimulateKeyDownEvent(const WebInputElement& element,
131 ui::KeyboardCode key_code) {
132 WebKit::WebKeyboardEvent key_event;
133 key_event.windowsKeyCode = key_code;
134 autofill_agent_->textFieldDidReceiveKeyDown(element, key_event);
135 }
136
137 void CheckTextFieldsState(const std::string& username,
138 bool username_autofilled,
139 const std::string& password,
140 bool password_autofilled) {
141 EXPECT_EQ(username,
142 static_cast<std::string>(username_element_.value().utf8()));
143 EXPECT_EQ(username_autofilled, username_element_.isAutofilled());
144 EXPECT_EQ(password,
145 static_cast<std::string>(password_element_.value().utf8()));
146 EXPECT_EQ(password_autofilled, password_element_.isAutofilled());
147 }
148
149 void CheckUsernameSelection(int start, int end) {
150 EXPECT_EQ(start, username_element_.selectionStart());
151 EXPECT_EQ(end, username_element_.selectionEnd());
152 }
153
154 string16 username1_;
155 string16 username2_;
156 string16 username3_;
157 string16 password1_;
158 string16 password2_;
159 string16 password3_;
160 PasswordFormFillData fill_data_;
161
162 WebInputElement username_element_;
163 WebInputElement password_element_;
164
165 private:
166 DISALLOW_COPY_AND_ASSIGN(PasswordAutofillManagerTest);
167 };
168
169 }
170
171 using autofill::PasswordAutofillManagerTest;
172
173 // Tests that the password login is autocompleted as expected when the browser
174 // sends back the password info.
175 TEST_F(PasswordAutofillManagerTest, InitialAutocomplete) {
176 /*
177 * Right now we are not sending the message to the browser because we are
178 * loading a data URL and the security origin canAccessPasswordManager()
179 * returns false. May be we should mock URL loading to cirmcuvent this?
180 TODO(jcivelli): find a way to make the security origin not deny access to the
181 password manager and then reenable this code.
182
183 // The form has been loaded, we should have sent the browser a message about
184 // the form.
185 const IPC::Message* msg = render_thread_.sink().GetFirstMessageMatching(
186 AutofillHostMsg_PasswordFormsFound::ID);
187 ASSERT_TRUE(msg != NULL);
188
189 Tuple1<std::vector<PasswordForm> > forms;
190 AutofillHostMsg_PasswordFormsFound::Read(msg, &forms);
191 ASSERT_EQ(1U, forms.a.size());
192 PasswordForm password_form = forms.a[0];
193 EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form.scheme);
194 EXPECT_EQ(ASCIIToUTF16(kUsernameName), password_form.username_element);
195 EXPECT_EQ(ASCIIToUTF16(kPasswordName), password_form.password_element);
196 */
197
198 // Simulate the browser sending back the login info, it triggers the
199 // autocomplete.
200 SimulateOnFillPasswordForm(fill_data_);
201
202 // The username and password should have been autocompleted.
203 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
204 }
205
206 // Tests that we correctly fill forms having an empty 'action' attribute.
207 TEST_F(PasswordAutofillManagerTest, InitialAutocompleteForEmptyAction) {
208 const char kEmptyActionFormHTML[] =
209 "<FORM name='LoginTestForm'>"
210 " <INPUT type='text' id='username'/>"
211 " <INPUT type='password' id='password'/>"
212 " <INPUT type='submit' value='Login'/>"
213 "</FORM>";
214 LoadHTML(kEmptyActionFormHTML);
215
216 // Retrieve the input elements so the test can access them.
217 WebDocument document = GetMainFrame()->document();
218 WebElement element =
219 document.getElementById(WebString::fromUTF8(kUsernameName));
220 ASSERT_FALSE(element.isNull());
221 username_element_ = element.to<WebKit::WebInputElement>();
222 element = document.getElementById(WebString::fromUTF8(kPasswordName));
223 ASSERT_FALSE(element.isNull());
224 password_element_ = element.to<WebKit::WebInputElement>();
225
226 // Set the expected form origin and action URLs.
227 std::string origin("data:text/html;charset=utf-8,");
228 origin += kEmptyActionFormHTML;
229 fill_data_.basic_data.origin = GURL(origin);
230 fill_data_.basic_data.action = GURL(origin);
231
232 // Simulate the browser sending back the login info, it triggers the
233 // autocomplete.
234 SimulateOnFillPasswordForm(fill_data_);
235
236 // The username and password should have been autocompleted.
237 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
238 }
239
240 // Tests that changing the username does not fill a read-only password field.
241 TEST_F(PasswordAutofillManagerTest, NoInitialAutocompleteForReadOnly) {
242 password_element_.setAttribute(WebString::fromUTF8("readonly"),
243 WebString::fromUTF8("true"));
244
245 // Simulate the browser sending back the login info, it triggers the
246 // autocomplete.
247 SimulateOnFillPasswordForm(fill_data_);
248
249 // Only the username should have been autocompleted.
250 // TODO(jcivelli): may be we should not event fill the username?
251 CheckTextFieldsState(kAliceUsername, true, "", false);
252 }
253
254 // Tests that having a non-matching username precludes the autocomplete.
255 TEST_F(PasswordAutofillManagerTest, NoInitialAutocompleteForFilledField) {
256 username_element_.setValue(WebString::fromUTF8("bogus"));
257
258 // Simulate the browser sending back the login info, it triggers the
259 // autocomplete.
260 SimulateOnFillPasswordForm(fill_data_);
261
262 // Neither field should be autocompleted.
263 CheckTextFieldsState("bogus", false, "", false);
264 }
265
266 // Tests that having a matching username does not preclude the autocomplete.
267 TEST_F(PasswordAutofillManagerTest, InitialAutocompleteForMatchingFilledField) {
268 username_element_.setValue(WebString::fromUTF8(kAliceUsername));
269
270 // Simulate the browser sending back the login info, it triggers the
271 // autocomplete.
272 SimulateOnFillPasswordForm(fill_data_);
273
274 // The username and password should have been autocompleted.
275 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
276 }
277
278 // Tests that editing the password clears the autocompleted password field.
279 TEST_F(PasswordAutofillManagerTest, PasswordClearOnEdit) {
280 // Simulate the browser sending back the login info, it triggers the
281 // autocomplete.
282 SimulateOnFillPasswordForm(fill_data_);
283
284 // Simulate the user changing the username to some unknown username.
285 SimulateUsernameChange("alicia", true);
286
287 // The password should have been cleared.
288 CheckTextFieldsState("alicia", false, "", false);
289 }
290
291 // Tests that we only autocomplete on focus lost and with a full username match
292 // when |wait_for_username| is true.
293 TEST_F(PasswordAutofillManagerTest, WaitUsername) {
294 // Simulate the browser sending back the login info.
295 fill_data_.wait_for_username = true;
296 SimulateOnFillPasswordForm(fill_data_);
297
298 // No auto-fill should have taken place.
299 CheckTextFieldsState("", false, "", false);
300
301 // No autocomplete should happen when text is entered in the username.
302 SimulateUsernameChange("a", true);
303 CheckTextFieldsState("a", false, "", false);
304 SimulateUsernameChange("al", true);
305 CheckTextFieldsState("al", false, "", false);
306 SimulateUsernameChange(kAliceUsername, true);
307 CheckTextFieldsState(kAliceUsername, false, "", false);
308
309 // Autocomplete should happen only when the username textfield is blurred with
310 // a full match.
311 username_element_.setValue("a");
312 autofill_agent_->textFieldDidEndEditing(username_element_);
313 CheckTextFieldsState("a", false, "", false);
314 username_element_.setValue("al");
315 autofill_agent_->textFieldDidEndEditing(username_element_);
316 CheckTextFieldsState("al", false, "", false);
317 username_element_.setValue("alices");
318 autofill_agent_->textFieldDidEndEditing(username_element_);
319 CheckTextFieldsState("alices", false, "", false);
320 username_element_.setValue(ASCIIToUTF16(kAliceUsername));
321 autofill_agent_->textFieldDidEndEditing(username_element_);
322 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
323 }
324
325 // Tests that inline autocompletion works properly.
326 TEST_F(PasswordAutofillManagerTest, InlineAutocomplete) {
327 // Simulate the browser sending back the login info.
328 SimulateOnFillPasswordForm(fill_data_);
329
330 // Clear the text fields to start fresh.
331 ClearUsernameAndPasswordFields();
332
333 // Simulate the user typing in the first letter of 'alice', a stored username.
334 SimulateUsernameChange("a", true);
335 // Both the username and password text fields should reflect selection of the
336 // stored login.
337 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
338 // And the selection should have been set to 'lice', the last 4 letters.
339 CheckUsernameSelection(1, 5);
340
341 // Now the user types the next letter of the same username, 'l'.
342 SimulateUsernameChange("al", true);
343 // Now the fields should have the same value, but the selection should have a
344 // different start value.
345 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
346 CheckUsernameSelection(2, 5);
347
348 // Test that deleting does not trigger autocomplete.
349 SimulateKeyDownEvent(username_element_, ui::VKEY_BACK);
350 SimulateUsernameChange("alic", true);
351 CheckTextFieldsState("alic", false, "", false);
352 CheckUsernameSelection(4, 4); // No selection.
353 // Reset the last pressed key to something other than backspace.
354 SimulateKeyDownEvent(username_element_, ui::VKEY_A);
355
356 // Now lets say the user goes astray from the stored username and types the
357 // letter 'f', spelling 'alf'. We don't know alf (that's just sad), so in
358 // practice the username should no longer be 'alice' and the selected range
359 // should be empty.
360 SimulateUsernameChange("alf", true);
361 CheckTextFieldsState("alf", false, "", false);
362 CheckUsernameSelection(3, 3); // No selection.
363
364 // Ok, so now the user removes all the text and enters the letter 'b'.
365 SimulateUsernameChange("b", true);
366 // The username and password fields should match the 'bob' entry.
367 CheckTextFieldsState(kBobUsername, true, kBobPassword, true);
368 CheckUsernameSelection(1, 3);
369
370 // Then, the user again removes all the text and types an uppercase 'C'.
371 SimulateUsernameChange("C", true);
372 // The username and password fields should match the 'Carol' entry.
373 CheckTextFieldsState(kCarolUsername, true, kCarolPassword, true);
374 CheckUsernameSelection(1, 5);
375 // Finally, the user removes all the text and types a lowercase 'c'. We only
376 // want case-sensitive autocompletion, so the username and the selected range
377 // should be empty.
378 SimulateUsernameChange("c", true);
379 CheckTextFieldsState("c", false, "", false);
380 CheckUsernameSelection(1, 1);
381 }
382
383 // Tests that accepting an item in the suggestion drop-down works.
384 TEST_F(PasswordAutofillManagerTest, SuggestionAccept) {
385 // Simulate the browser sending back the login info.
386 SimulateOnFillPasswordForm(fill_data_);
387
388 // Clear the text fields to start fresh.
389 ClearUsernameAndPasswordFields();
390
391 // To simulate accepting an item in the suggestion drop-down we just mimic
392 // what the WebView does: it sets the element value then calls
393 // didAcceptAutofillSuggestion on the renderer.
394 autofill_agent_->didAcceptAutofillSuggestion(username_element_,
395 ASCIIToUTF16(kAliceUsername),
396 WebKit::WebString(),
397 0,
398 0);
399 // Autocomplete should have kicked in.
400 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true);
401 }
402
403 // Tests that selecting an item in the suggestion drop-down no-ops.
404 TEST_F(PasswordAutofillManagerTest, SuggestionSelect) {
405 // Simulate the browser sending back the login info.
406 SimulateOnFillPasswordForm(fill_data_);
407
408 // Clear the text fields to start fresh.
409 ClearUsernameAndPasswordFields();
410
411 // To simulate accepting an item in the suggestion drop-down we just mimic
412 // what the WebView does: it sets the element value then calls
413 // didSelectAutofillSuggestion on the renderer.
414 autofill_agent_->didSelectAutofillSuggestion(username_element_,
415 ASCIIToUTF16(kAliceUsername),
416 WebKit::WebString(),
417 0);
418 // Autocomplete should not have kicked in.
419 CheckTextFieldsState("", false, "", false);
420 }
OLDNEW
« no previous file with comments | « chrome/renderer/autofill/password_autofill_manager_browsertest.cc ('k') | chrome/test/live_sync/autofill_helper.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698