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. 4 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" 23 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; 31 32 namespace { 33 34 // The name of the username/password element in the form. 35 const char* const kUsernameName = "username"; 36 const char* const kPasswordName = "password"; 37 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"; 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 PasswordAutofillAgentTest : public ChromeRenderViewTest { 59 public: 60 PasswordAutofillAgentTest() { 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 ChromeRenderViewTest::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 alternate_username3_ = ASCIIToUTF16(kCarolAlternateUsername); 83 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); 88 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); 94 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; 101 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_); 107 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,"); 111 origin += kFormHTML; 112 fill_data_.basic_data.origin = GURL(origin); 113 fill_data_.basic_data.action = GURL("http://www.bidule.com"); 114 115 LoadHTML(kFormHTML); 116 117 // Now retrieves the input elements so the test can access them. 118 WebDocument document = GetMainFrame()->document(); 119 WebElement element = 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>(); 126 } 127 128 virtual void TearDown() { 129 username_element_.reset(); 130 password_element_.reset(); 131 ChromeRenderViewTest::TearDown(); 132 } 133 134 void ClearUsernameAndPasswordFields() { 135 username_element_.setValue(""); 136 username_element_.setAutofilled(false); 137 password_element_.setValue(""); 138 password_element_.setAutofilled(false); 139 } 140 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(); 154 } 155 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); 161 } 162 163 void CheckTextFieldsState(const std::string& username, 164 bool username_autofilled, 165 const std::string& password, 166 bool password_autofilled) { 167 EXPECT_EQ(username, 168 static_cast<std::string>(username_element_.value().utf8())); 169 EXPECT_EQ(username_autofilled, username_element_.isAutofilled()); 170 EXPECT_EQ(password, 171 static_cast<std::string>(password_element_.value().utf8())); 172 EXPECT_EQ(password_autofilled, password_element_.isAutofilled()); 173 } 174 175 void CheckUsernameSelection(int start, int end) { 176 EXPECT_EQ(start, username_element_.selectionStart()); 177 EXPECT_EQ(end, username_element_.selectionEnd()); 178 } 179 180 string16 username1_; 181 string16 username2_; 182 string16 username3_; 183 string16 password1_; 184 string16 password2_; 185 string16 password3_; 186 string16 alternate_username3_; 187 PasswordFormFillData fill_data_; 188 189 WebInputElement username_element_; 190 WebInputElement password_element_; 191 192 private: 193 DISALLOW_COPY_AND_ASSIGN(PasswordAutofillAgentTest); 194 }; 195 196 // Tests that the password login is autocompleted as expected when the browser 197 // sends back the password info. 198 TEST_F(PasswordAutofillAgentTest, InitialAutocomplete) { 199 /* 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. 205 206 // The form has been loaded, we should have sent the browser a message about 207 // the form. 208 const IPC::Message* msg = render_thread_.sink().GetFirstMessageMatching( 209 AutofillHostMsg_PasswordFormsParsed::ID); 210 ASSERT_TRUE(msg != NULL); 211 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); 219 */ 220 221 // Simulate the browser sending back the login info, it triggers the 222 // autocomplete. 223 SimulateOnFillPasswordForm(fill_data_); 224 225 // The username and password should have been autocompleted. 226 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true); 227 } 228 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'/>" 236 "</FORM>"; 237 LoadHTML(kEmptyActionFormHTML); 238 239 // Retrieve the input elements so the test can access them. 240 WebDocument document = GetMainFrame()->document(); 241 WebElement element = 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>(); 248 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); 254 255 // Simulate the browser sending back the login info, it triggers the 256 // autocomplete. 257 SimulateOnFillPasswordForm(fill_data_); 258 259 // The username and password should have been autocompleted. 260 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true); 261 } 262 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")); 268 269 // Simulate the browser sending back the login info, it triggers the 270 // autocomplete. 271 SimulateOnFillPasswordForm(fill_data_); 272 273 CheckTextFieldsState(std::string(), false, std::string(), false); 274 } 275 276 // Tests that having a non-matching username precludes the autocomplete. 277 TEST_F(PasswordAutofillAgentTest, NoInitialAutocompleteForFilledField) { 278 username_element_.setValue(WebString::fromUTF8("bogus")); 279 280 // Simulate the browser sending back the login info, it triggers the 281 // autocomplete. 282 SimulateOnFillPasswordForm(fill_data_); 283 284 // Neither field should be autocompleted. 285 CheckTextFieldsState("bogus", false, std::string(), false); 286 } 287 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")); 293 294 // Simulate the browser sending back the login info, it triggers the 295 // autocomplete. 296 SimulateOnFillPasswordForm(fill_data_); 297 298 CheckTextFieldsState(std::string(), false, std::string(), false); 299 } 300 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'/>" 307 "</FORM>"; 308 LoadHTML(kTextFieldPasswordFormHTML); 309 310 // Retrieve the input elements so the test can access them. 311 WebDocument document = GetMainFrame()->document(); 312 WebElement element = 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>(); 319 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); 324 325 SimulateOnFillPasswordForm(fill_data_); 326 327 // Fields should still be empty. 328 CheckTextFieldsState(std::string(), false, std::string(), false); 329 } 330 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'/>" 337 "</FORM>"; 338 LoadHTML(kPasswordFieldUsernameFormHTML); 339 340 // Retrieve the input elements so the test can access them. 341 WebDocument document = GetMainFrame()->document(); 342 WebElement element = 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>(); 349 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); 354 355 SimulateOnFillPasswordForm(fill_data_); 356 357 // Fields should still be empty. 358 CheckTextFieldsState(std::string(), false, std::string(), false); 359 } 360 361 // Tests that having a matching username does not preclude the autocomplete. 362 TEST_F(PasswordAutofillAgentTest, InitialAutocompleteForMatchingFilledField) { 363 username_element_.setValue(WebString::fromUTF8(kAliceUsername)); 364 365 // Simulate the browser sending back the login info, it triggers the 366 // autocomplete. 367 SimulateOnFillPasswordForm(fill_data_); 368 369 // The username and password should have been autocompleted. 370 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true); 371 } 372 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 376 // autocomplete. 377 SimulateOnFillPasswordForm(fill_data_); 378 379 // Simulate the user changing the username to some unknown username. 380 SimulateUsernameChange("alicia", true); 381 382 // The password should have been cleared. 383 CheckTextFieldsState("alicia", false, std::string(), false); 384 } 385 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_); 392 393 // No auto-fill should have taken place. 394 CheckTextFieldsState(std::string(), false, std::string(), false); 395 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); 403 404 // Autocomplete should happen only when the username textfield is blurred with 405 // a full match. 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); 418 } 419 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_); 424 425 // Clear the text fields to start fresh. 426 ClearUsernameAndPasswordFields(); 427 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 431 // stored login. 432 CheckTextFieldsState(kAliceUsername, true, kAlicePassword, true); 433 // And the selection should have been set to 'lice', the last 4 letters. 434 CheckUsernameSelection(1, 5); 435 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); 442 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); 450 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 454 // should be empty. 455 SimulateUsernameChange("alf", true); 456 CheckTextFieldsState("alf", false, std::string(), false); 457 CheckUsernameSelection(3, 3); // No selection. 458 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); 464 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 472 // should be empty. 473 SimulateUsernameChange("c", true); 474 CheckTextFieldsState("c", false, std::string(), false); 475 CheckUsernameSelection(1, 1); 476 477 // Check that we complete other_possible_usernames as well. 478 SimulateUsernameChange("R", true); 479 CheckTextFieldsState(kCarolAlternateUsername, true, kCarolPassword, true); 480 CheckUsernameSelection(1, 17); 481 } 482 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_); 487 488 // Clear the text fields to start fresh. 489 ClearUsernameAndPasswordFields(); 490 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), 496 WebKit::WebString(), 497 0); 498 // Autocomplete should not have kicked in. 499 CheckTextFieldsState(std::string(), false, std::string(), false); 500 } 501 502 } // namespace autofill 503