1 // Copyright (c) 2012 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/string_util.h"
6 #include "base/strings/utf_string_conversions.h"
7 #include "chrome/test/base/chrome_render_view_test.h"
8 #include "components/autofill/content/renderer/autofill_agent.h"
9 #include "components/autofill/content/renderer/password_autofill_agent.h"
10 #include "components/autofill/core/common/autofill_messages.h"
11 #include "components/autofill/core/common/form_data.h"
12 #include "components/autofill/core/common/form_field_data.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "third_party/WebKit/public/platform/WebString.h"
15 #include "third_party/WebKit/public/platform/WebVector.h"
16 #include "third_party/WebKit/public/web/WebDocument.h"
17 #include "third_party/WebKit/public/web/WebElement.h"
18 #include "third_party/WebKit/public/web/WebFormElement.h"
19 #include "third_party/WebKit/public/web/WebInputElement.h"
20 #include "third_party/WebKit/public/web/WebNode.h"
21 #include "third_party/WebKit/public/web/WebView.h"
22 #include "ui/base/keycodes/keyboard_codes.h"
24 using content::PasswordForm
;
25 using WebKit::WebDocument
;
26 using WebKit::WebElement
;
27 using WebKit::WebFrame
;
28 using WebKit::WebInputElement
;
29 using WebKit::WebString
;
30 using WebKit::WebView
;
34 // The name of the username/password element in the form.
35 const char* const kUsernameName
= "username";
36 const char* const kPasswordName
= "password";
38 const char* const kAliceUsername
= "alice";
39 const char* const kAlicePassword
= "password";
40 const char* const kBobUsername
= "bob";
41 const char* const kBobPassword
= "secret";
42 const char* const kCarolUsername
= "Carol";
43 const char* const kCarolPassword
= "test";
44 const char* const kCarolAlternateUsername
= "RealCarolUsername";
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'/>"
58 class PasswordAutofillAgentTest
: public ChromeRenderViewTest
{
60 PasswordAutofillAgentTest() {
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()
66 void SimulateOnFillPasswordForm(
67 const PasswordFormFillData
& fill_data
) {
68 AutofillMsg_FillPasswordForm
msg(0, fill_data
);
69 password_autofill_
->OnMessageReceived(msg
);
72 virtual void SetUp() {
73 ChromeRenderViewTest::SetUp();
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 alternate_username3_
= ASCIIToUTF16(kCarolAlternateUsername
);
84 FormFieldData username_field
;
85 username_field
.name
= ASCIIToUTF16(kUsernameName
);
86 username_field
.value
= username1_
;
87 fill_data_
.basic_data
.fields
.push_back(username_field
);
89 FormFieldData password_field
;
90 password_field
.name
= ASCIIToUTF16(kPasswordName
);
91 password_field
.value
= password1_
;
92 password_field
.form_control_type
= "password";
93 fill_data_
.basic_data
.fields
.push_back(password_field
);
95 PasswordAndRealm password2
;
96 password2
.password
= password2_
;
97 fill_data_
.additional_logins
[username2_
] = password2
;
98 PasswordAndRealm password3
;
99 password3
.password
= password3_
;
100 fill_data_
.additional_logins
[username3_
] = password3
;
102 UsernamesCollectionKey key
;
103 key
.username
= username3_
;
104 key
.password
= password3_
;
105 key
.realm
= "google.com";
106 fill_data_
.other_possible_usernames
[key
].push_back(alternate_username3_
);
108 // We need to set the origin so it matches the frame URL and the action so
109 // it matches the form action, otherwise we won't autocomplete.
110 std::string
origin("data:text/html;charset=utf-8,");
112 fill_data_
.basic_data
.origin
= GURL(origin
);
113 fill_data_
.basic_data
.action
= GURL("http://www.bidule.com");
117 // Now retrieves the input elements so the test can access them.
118 WebDocument document
= GetMainFrame()->document();
120 document
.getElementById(WebString::fromUTF8(kUsernameName
));
121 ASSERT_FALSE(element
.isNull());
122 username_element_
= element
.to
<WebKit::WebInputElement
>();
123 element
= document
.getElementById(WebString::fromUTF8(kPasswordName
));
124 ASSERT_FALSE(element
.isNull());
125 password_element_
= element
.to
<WebKit::WebInputElement
>();
128 virtual void TearDown() {
129 username_element_
.reset();
130 password_element_
.reset();
131 ChromeRenderViewTest::TearDown();
134 void ClearUsernameAndPasswordFields() {
135 username_element_
.setValue("");
136 username_element_
.setAutofilled(false);
137 password_element_
.setValue("");
138 password_element_
.setAutofilled(false);
141 void SimulateUsernameChange(const std::string
& username
,
142 bool move_caret_to_end
) {
143 username_element_
.setValue(WebString::fromUTF8(username
));
144 // The field must have focus or AutofillAgent will think the
145 // change should be ignored.
146 while (!username_element_
.focused())
147 GetMainFrame()->document().frame()->view()->advanceFocus(false);
148 if (move_caret_to_end
)
149 username_element_
.setSelectionRange(username
.length(), username
.length());
150 autofill_agent_
->textFieldDidChange(username_element_
);
151 // Processing is delayed because of a WebKit bug, see
152 // PasswordAutocompleteManager::TextDidChangeInTextField() for details.
153 base::MessageLoop::current()->RunUntilIdle();
156 void SimulateKeyDownEvent(const WebInputElement
& element
,
157 ui::KeyboardCode key_code
) {
158 WebKit::WebKeyboardEvent key_event
;
159 key_event
.windowsKeyCode
= key_code
;
160 autofill_agent_
->textFieldDidReceiveKeyDown(element
, key_event
);
163 void CheckTextFieldsState(const std::string
& username
,
164 bool username_autofilled
,
165 const std::string
& password
,
166 bool password_autofilled
) {
168 static_cast<std::string
>(username_element_
.value().utf8()));
169 EXPECT_EQ(username_autofilled
, username_element_
.isAutofilled());
171 static_cast<std::string
>(password_element_
.value().utf8()));
172 EXPECT_EQ(password_autofilled
, password_element_
.isAutofilled());
175 void CheckUsernameSelection(int start
, int end
) {
176 EXPECT_EQ(start
, username_element_
.selectionStart());
177 EXPECT_EQ(end
, username_element_
.selectionEnd());
186 string16 alternate_username3_
;
187 PasswordFormFillData fill_data_
;
189 WebInputElement username_element_
;
190 WebInputElement password_element_
;
193 DISALLOW_COPY_AND_ASSIGN(PasswordAutofillAgentTest
);
196 // Tests that the password login is autocompleted as expected when the browser
197 // sends back the password info.
198 TEST_F(PasswordAutofillAgentTest
, InitialAutocomplete
) {
200 * Right now we are not sending the message to the browser because we are
201 * loading a data URL and the security origin canAccessPasswordManager()
202 * returns false. May be we should mock URL loading to cirmcuvent this?
203 TODO(jcivelli): find a way to make the security origin not deny access to the
204 password manager and then reenable this code.
206 // The form has been loaded, we should have sent the browser a message about
208 const IPC::Message* msg = render_thread_.sink().GetFirstMessageMatching(
209 AutofillHostMsg_PasswordFormsParsed::ID);
210 ASSERT_TRUE(msg != NULL);
212 Tuple1<std::vector<PasswordForm> > forms;
213 AutofillHostMsg_PasswordFormsParsed::Read(msg, &forms);
214 ASSERT_EQ(1U, forms.a.size());
215 PasswordForm password_form = forms.a[0];
216 EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form.scheme);
217 EXPECT_EQ(ASCIIToUTF16(kUsernameName), password_form.username_element);
218 EXPECT_EQ(ASCIIToUTF16(kPasswordName), password_form.password_element);
221 // Simulate the browser sending back the login info, it triggers the
223 SimulateOnFillPasswordForm(fill_data_
);
225 // The username and password should have been autocompleted.
226 CheckTextFieldsState(kAliceUsername
, true, kAlicePassword
, true);
229 // Tests that we correctly fill forms having an empty 'action' attribute.
230 TEST_F(PasswordAutofillAgentTest
, InitialAutocompleteForEmptyAction
) {
231 const char kEmptyActionFormHTML
[] =
232 "<FORM name='LoginTestForm'>"
233 " <INPUT type='text' id='username'/>"
234 " <INPUT type='password' id='password'/>"
235 " <INPUT type='submit' value='Login'/>"
237 LoadHTML(kEmptyActionFormHTML
);
239 // Retrieve the input elements so the test can access them.
240 WebDocument document
= GetMainFrame()->document();
242 document
.getElementById(WebString::fromUTF8(kUsernameName
));
243 ASSERT_FALSE(element
.isNull());
244 username_element_
= element
.to
<WebKit::WebInputElement
>();
245 element
= document
.getElementById(WebString::fromUTF8(kPasswordName
));
246 ASSERT_FALSE(element
.isNull());
247 password_element_
= element
.to
<WebKit::WebInputElement
>();
249 // Set the expected form origin and action URLs.
250 std::string
origin("data:text/html;charset=utf-8,");
251 origin
+= kEmptyActionFormHTML
;
252 fill_data_
.basic_data
.origin
= GURL(origin
);
253 fill_data_
.basic_data
.action
= GURL(origin
);
255 // Simulate the browser sending back the login info, it triggers the
257 SimulateOnFillPasswordForm(fill_data_
);
259 // The username and password should have been autocompleted.
260 CheckTextFieldsState(kAliceUsername
, true, kAlicePassword
, true);
263 // Tests that if a password or input element is marked as readonly, neither
264 // field is autofilled on page load.
265 TEST_F(PasswordAutofillAgentTest
, NoInitialAutocompleteForReadOnly
) {
266 password_element_
.setAttribute(WebString::fromUTF8("readonly"),
267 WebString::fromUTF8("true"));
269 // Simulate the browser sending back the login info, it triggers the
271 SimulateOnFillPasswordForm(fill_data_
);
273 CheckTextFieldsState(std::string(), false, std::string(), false);
276 // Tests that having a non-matching username precludes the autocomplete.
277 TEST_F(PasswordAutofillAgentTest
, NoInitialAutocompleteForFilledField
) {
278 username_element_
.setValue(WebString::fromUTF8("bogus"));
280 // Simulate the browser sending back the login info, it triggers the
282 SimulateOnFillPasswordForm(fill_data_
);
284 // Neither field should be autocompleted.
285 CheckTextFieldsState("bogus", false, std::string(), false);
288 // Tests that we do not autofill username/passwords if marked as
289 // autocomplete="off".
290 TEST_F(PasswordAutofillAgentTest
, NoInitialAutocompleteForAutocompleteOff
) {
291 username_element_
.setAttribute(WebString::fromUTF8("autocomplete"),
292 WebString::fromUTF8("off"));
294 // Simulate the browser sending back the login info, it triggers the
296 SimulateOnFillPasswordForm(fill_data_
);
298 CheckTextFieldsState(std::string(), false, std::string(), false);
301 TEST_F(PasswordAutofillAgentTest
, NoAutocompleteForTextFieldPasswords
) {
302 const char kTextFieldPasswordFormHTML
[] =
303 "<FORM name='LoginTestForm' action='http://www.bidule.com'>"
304 " <INPUT type='text' id='username'/>"
305 " <INPUT type='text' id='password'/>"
306 " <INPUT type='submit' value='Login'/>"
308 LoadHTML(kTextFieldPasswordFormHTML
);
310 // Retrieve the input elements so the test can access them.
311 WebDocument document
= GetMainFrame()->document();
313 document
.getElementById(WebString::fromUTF8(kUsernameName
));
314 ASSERT_FALSE(element
.isNull());
315 username_element_
= element
.to
<WebKit::WebInputElement
>();
316 element
= document
.getElementById(WebString::fromUTF8(kPasswordName
));
317 ASSERT_FALSE(element
.isNull());
318 password_element_
= element
.to
<WebKit::WebInputElement
>();
320 // Set the expected form origin URL.
321 std::string
origin("data:text/html;charset=utf-8,");
322 origin
+= kTextFieldPasswordFormHTML
;
323 fill_data_
.basic_data
.origin
= GURL(origin
);
325 SimulateOnFillPasswordForm(fill_data_
);
327 // Fields should still be empty.
328 CheckTextFieldsState(std::string(), false, std::string(), false);
331 TEST_F(PasswordAutofillAgentTest
, NoAutocompleteForPasswordFieldUsernames
) {
332 const char kPasswordFieldUsernameFormHTML
[] =
333 "<FORM name='LoginTestForm' action='http://www.bidule.com'>"
334 " <INPUT type='password' id='username'/>"
335 " <INPUT type='password' id='password'/>"
336 " <INPUT type='submit' value='Login'/>"
338 LoadHTML(kPasswordFieldUsernameFormHTML
);
340 // Retrieve the input elements so the test can access them.
341 WebDocument document
= GetMainFrame()->document();
343 document
.getElementById(WebString::fromUTF8(kUsernameName
));
344 ASSERT_FALSE(element
.isNull());
345 username_element_
= element
.to
<WebKit::WebInputElement
>();
346 element
= document
.getElementById(WebString::fromUTF8(kPasswordName
));
347 ASSERT_FALSE(element
.isNull());
348 password_element_
= element
.to
<WebKit::WebInputElement
>();
350 // Set the expected form origin URL.
351 std::string
origin("data:text/html;charset=utf-8,");
352 origin
+= kPasswordFieldUsernameFormHTML
;
353 fill_data_
.basic_data
.origin
= GURL(origin
);
355 SimulateOnFillPasswordForm(fill_data_
);
357 // Fields should still be empty.
358 CheckTextFieldsState(std::string(), false, std::string(), false);
361 // Tests that having a matching username does not preclude the autocomplete.
362 TEST_F(PasswordAutofillAgentTest
, InitialAutocompleteForMatchingFilledField
) {
363 username_element_
.setValue(WebString::fromUTF8(kAliceUsername
));
365 // Simulate the browser sending back the login info, it triggers the
367 SimulateOnFillPasswordForm(fill_data_
);
369 // The username and password should have been autocompleted.
370 CheckTextFieldsState(kAliceUsername
, true, kAlicePassword
, true);
373 // Tests that editing the password clears the autocompleted password field.
374 TEST_F(PasswordAutofillAgentTest
, PasswordClearOnEdit
) {
375 // Simulate the browser sending back the login info, it triggers the
377 SimulateOnFillPasswordForm(fill_data_
);
379 // Simulate the user changing the username to some unknown username.
380 SimulateUsernameChange("alicia", true);
382 // The password should have been cleared.
383 CheckTextFieldsState("alicia", false, std::string(), false);
386 // Tests that we only autocomplete on focus lost and with a full username match
387 // when |wait_for_username| is true.
388 TEST_F(PasswordAutofillAgentTest
, WaitUsername
) {
389 // Simulate the browser sending back the login info.
390 fill_data_
.wait_for_username
= true;
391 SimulateOnFillPasswordForm(fill_data_
);
393 // No auto-fill should have taken place.
394 CheckTextFieldsState(std::string(), false, std::string(), false);
396 // No autocomplete should happen when text is entered in the username.
397 SimulateUsernameChange("a", true);
398 CheckTextFieldsState("a", false, std::string(), false);
399 SimulateUsernameChange("al", true);
400 CheckTextFieldsState("al", false, std::string(), false);
401 SimulateUsernameChange(kAliceUsername
, true);
402 CheckTextFieldsState(kAliceUsername
, false, std::string(), false);
404 // Autocomplete should happen only when the username textfield is blurred with
406 username_element_
.setValue("a");
407 autofill_agent_
->textFieldDidEndEditing(username_element_
);
408 CheckTextFieldsState("a", false, std::string(), false);
409 username_element_
.setValue("al");
410 autofill_agent_
->textFieldDidEndEditing(username_element_
);
411 CheckTextFieldsState("al", false, std::string(), false);
412 username_element_
.setValue("alices");
413 autofill_agent_
->textFieldDidEndEditing(username_element_
);
414 CheckTextFieldsState("alices", false, std::string(), false);
415 username_element_
.setValue(ASCIIToUTF16(kAliceUsername
));
416 autofill_agent_
->textFieldDidEndEditing(username_element_
);
417 CheckTextFieldsState(kAliceUsername
, true, kAlicePassword
, true);
420 // Tests that inline autocompletion works properly.
421 TEST_F(PasswordAutofillAgentTest
, InlineAutocomplete
) {
422 // Simulate the browser sending back the login info.
423 SimulateOnFillPasswordForm(fill_data_
);
425 // Clear the text fields to start fresh.
426 ClearUsernameAndPasswordFields();
428 // Simulate the user typing in the first letter of 'alice', a stored username.
429 SimulateUsernameChange("a", true);
430 // Both the username and password text fields should reflect selection of the
432 CheckTextFieldsState(kAliceUsername
, true, kAlicePassword
, true);
433 // And the selection should have been set to 'lice', the last 4 letters.
434 CheckUsernameSelection(1, 5);
436 // Now the user types the next letter of the same username, 'l'.
437 SimulateUsernameChange("al", true);
438 // Now the fields should have the same value, but the selection should have a
439 // different start value.
440 CheckTextFieldsState(kAliceUsername
, true, kAlicePassword
, true);
441 CheckUsernameSelection(2, 5);
443 // Test that deleting does not trigger autocomplete.
444 SimulateKeyDownEvent(username_element_
, ui::VKEY_BACK
);
445 SimulateUsernameChange("alic", true);
446 CheckTextFieldsState("alic", false, std::string(), false);
447 CheckUsernameSelection(4, 4); // No selection.
448 // Reset the last pressed key to something other than backspace.
449 SimulateKeyDownEvent(username_element_
, ui::VKEY_A
);
451 // Now lets say the user goes astray from the stored username and types the
452 // letter 'f', spelling 'alf'. We don't know alf (that's just sad), so in
453 // practice the username should no longer be 'alice' and the selected range
455 SimulateUsernameChange("alf", true);
456 CheckTextFieldsState("alf", false, std::string(), false);
457 CheckUsernameSelection(3, 3); // No selection.
459 // Ok, so now the user removes all the text and enters the letter 'b'.
460 SimulateUsernameChange("b", true);
461 // The username and password fields should match the 'bob' entry.
462 CheckTextFieldsState(kBobUsername
, true, kBobPassword
, true);
463 CheckUsernameSelection(1, 3);
465 // Then, the user again removes all the text and types an uppercase 'C'.
466 SimulateUsernameChange("C", true);
467 // The username and password fields should match the 'Carol' entry.
468 CheckTextFieldsState(kCarolUsername
, true, kCarolPassword
, true);
469 CheckUsernameSelection(1, 5);
470 // The user removes all the text and types a lowercase 'c'. We only
471 // want case-sensitive autocompletion, so the username and the selected range
473 SimulateUsernameChange("c", true);
474 CheckTextFieldsState("c", false, std::string(), false);
475 CheckUsernameSelection(1, 1);
477 // Check that we complete other_possible_usernames as well.
478 SimulateUsernameChange("R", true);
479 CheckTextFieldsState(kCarolAlternateUsername
, true, kCarolPassword
, true);
480 CheckUsernameSelection(1, 17);
483 // Tests that selecting an item in the suggestion drop-down no-ops.
484 TEST_F(PasswordAutofillAgentTest
, SuggestionSelect
) {
485 // Simulate the browser sending back the login info.
486 SimulateOnFillPasswordForm(fill_data_
);
488 // Clear the text fields to start fresh.
489 ClearUsernameAndPasswordFields();
491 // To simulate accepting an item in the suggestion drop-down we just mimic
492 // what the WebView does: it sets the element value then calls
493 // didSelectAutofillSuggestion on the renderer.
494 autofill_agent_
->didSelectAutofillSuggestion(username_element_
,
495 ASCIIToUTF16(kAliceUsername
),
498 // Autocomplete should not have kicked in.
499 CheckTextFieldsState(std::string(), false, std::string(), false);
502 } // namespace autofill