1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <string> 6 7 #include "base/command_line.h" 8 #include "base/metrics/histogram_samples.h" 9 #include "base/metrics/statistics_recorder.h" 10 #include "base/strings/stringprintf.h" 11 #include "chrome/browser/chrome_notification_types.h" 12 #include "chrome/browser/infobars/infobar_service.h" 13 #include "chrome/browser/password_manager/chrome_password_manager_client.h" 14 #include "chrome/browser/password_manager/password_store_factory.h" 15 #include "chrome/browser/password_manager/test_password_store_service.h" 16 #include "chrome/browser/ui/browser.h" 17 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h" 18 #include "chrome/browser/ui/tabs/tab_strip_model.h" 19 #include "chrome/common/chrome_version_info.h" 20 #include "chrome/test/base/in_process_browser_test.h" 21 #include "chrome/test/base/test_switches.h" 22 #include "chrome/test/base/ui_test_utils.h" 23 #include "components/autofill/core/browser/autofill_test_utils.h" 24 #include "components/infobars/core/confirm_infobar_delegate.h" 25 #include "components/infobars/core/infobar.h" 26 #include "components/infobars/core/infobar_manager.h" 27 #include "components/password_manager/core/browser/test_password_store.h" 28 #include "components/password_manager/core/common/password_manager_switches.h" 29 #include "content/public/browser/render_view_host.h" 30 #include "content/public/browser/web_contents.h" 31 #include "content/public/browser/web_contents_observer.h" 32 #include "content/public/test/browser_test_utils.h" 33 #include "content/public/test/test_utils.h" 34 #include "net/test/embedded_test_server/embedded_test_server.h" 35 #include "net/url_request/test_url_fetcher_factory.h" 36 #include "testing/gmock/include/gmock/gmock.h" 37 #include "third_party/WebKit/public/web/WebInputEvent.h" 38 #include "ui/events/keycodes/keyboard_codes.h" 39 #include "ui/gfx/geometry/point.h" 40 41 42 // NavigationObserver --------------------------------------------------------- 43 44 namespace { 45 46 // Observer that waits for navigation to complete and for the password infobar 47 // to be shown. 48 class NavigationObserver : public content::WebContentsObserver, 49 public infobars::InfoBarManager::Observer { 50 public: 51 explicit NavigationObserver(content::WebContents* web_contents) 52 : content::WebContentsObserver(web_contents), 53 message_loop_runner_(new content::MessageLoopRunner), 54 infobar_shown_(false), 55 infobar_removed_(false), 56 should_automatically_accept_infobar_(true), 57 infobar_service_(InfoBarService::FromWebContents(web_contents)) { 58 infobar_service_->AddObserver(this); 59 } 60 61 virtual ~NavigationObserver() { 62 if (infobar_service_) 63 infobar_service_->RemoveObserver(this); 64 } 65 66 // Normally Wait() will not return until a main frame navigation occurs. 67 // If a path is set, Wait() will return after this path has been seen, 68 // regardless of the frame that navigated. Useful for multi-frame pages. 69 void SetPathToWaitFor(const std::string& path) { 70 wait_for_path_ = path; 71 } 72 73 // content::WebContentsObserver: 74 virtual void DidFinishLoad( 75 int64 frame_id, 76 const GURL& validated_url, 77 bool is_main_frame, 78 content::RenderViewHost* render_view_host) OVERRIDE { 79 if (!wait_for_path_.empty()) { 80 if (validated_url.path() == wait_for_path_) 81 message_loop_runner_->Quit(); 82 } else if (is_main_frame) { 83 message_loop_runner_->Quit(); 84 } 85 } 86 87 bool infobar_shown() const { return infobar_shown_; } 88 bool infobar_removed() const { return infobar_removed_; } 89 90 void disable_should_automatically_accept_infobar() { 91 should_automatically_accept_infobar_ = false; 92 } 93 94 void Wait() { 95 message_loop_runner_->Run(); 96 } 97 98 private: 99 // infobars::InfoBarManager::Observer: 100 virtual void OnInfoBarAdded(infobars::InfoBar* infobar) OVERRIDE { 101 if (should_automatically_accept_infobar_) { 102 infobar_service_->infobar_at(0)->delegate()-> 103 AsConfirmInfoBarDelegate()->Accept(); 104 } 105 infobar_shown_ = true; 106 } 107 108 virtual void OnInfoBarRemoved(infobars::InfoBar* infobar, 109 bool animate) OVERRIDE { 110 infobar_removed_ = true; 111 } 112 113 virtual void OnManagerShuttingDown( 114 infobars::InfoBarManager* manager) OVERRIDE { 115 ASSERT_EQ(infobar_service_, manager); 116 infobar_service_->RemoveObserver(this); 117 infobar_service_ = NULL; 118 } 119 120 std::string wait_for_path_; 121 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 122 bool infobar_shown_; 123 bool infobar_removed_; 124 // If |should_automatically_accept_infobar_| is true, then whenever the test 125 // sees an infobar added, it will click its accepting button. Default = true. 126 bool should_automatically_accept_infobar_; 127 InfoBarService* infobar_service_; 128 129 DISALLOW_COPY_AND_ASSIGN(NavigationObserver); 130 }; 131 132 } // namespace 133 134 135 // PasswordManagerBrowserTest ------------------------------------------------- 136 137 class PasswordManagerBrowserTest : public InProcessBrowserTest { 138 public: 139 PasswordManagerBrowserTest() {} 140 virtual ~PasswordManagerBrowserTest() {} 141 142 // InProcessBrowserTest: 143 virtual void SetUpOnMainThread() OVERRIDE { 144 // Use TestPasswordStore to remove a possible race. Normally the 145 // PasswordStore does its database manipulation on the DB thread, which 146 // creates a possible race during navigation. Specifically the 147 // PasswordManager will ignore any forms in a page if the load from the 148 // PasswordStore has not completed. 149 PasswordStoreFactory::GetInstance()->SetTestingFactory( 150 browser()->profile(), TestPasswordStoreService::Build); 151 } 152 153 protected: 154 content::WebContents* WebContents() { 155 return browser()->tab_strip_model()->GetActiveWebContents(); 156 } 157 158 content::RenderViewHost* RenderViewHost() { 159 return WebContents()->GetRenderViewHost(); 160 } 161 162 ManagePasswordsUIController* controller() { 163 return ManagePasswordsUIController::FromWebContents(WebContents()); 164 } 165 166 // Wrapper around ui_test_utils::NavigateToURL that waits until 167 // DidFinishLoad() fires. Normally this function returns after 168 // DidStopLoading(), which caused flakiness as the NavigationObserver 169 // would sometimes see the DidFinishLoad event from a previous navigation and 170 // return immediately. 171 void NavigateToFile(const std::string& path) { 172 if (!embedded_test_server()->Started()) 173 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 174 175 ASSERT_FALSE(CommandLine::ForCurrentProcess()->HasSwitch( 176 password_manager::switches::kEnableAutomaticPasswordSaving)); 177 NavigationObserver observer(WebContents()); 178 GURL url = embedded_test_server()->GetURL(path); 179 ui_test_utils::NavigateToURL(browser(), url); 180 observer.Wait(); 181 } 182 183 // Waits until the "value" attribute of the HTML element with |element_id| is 184 // equal to |expected_value|. If the current value is not as expected, this 185 // waits until the "change" event is fired for the element. This also 186 // guarantees that once the real value matches the expected, the JavaScript 187 // event loop is spun to allow all other possible events to take place. 188 void WaitForElementValue(const std::string& element_id, 189 const std::string& expected_value); 190 // Checks that the current "value" attribute of the HTML element with 191 // |element_id| is equal to |expected_value|. 192 void CheckElementValue(const std::string& element_id, 193 const std::string& expected_value); 194 195 private: 196 DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest); 197 }; 198 199 void PasswordManagerBrowserTest::WaitForElementValue( 200 const std::string& element_id, 201 const std::string& expected_value) { 202 enum ReturnCodes { // Possible results of the JavaScript code. 203 RETURN_CODE_OK, 204 RETURN_CODE_NO_ELEMENT, 205 RETURN_CODE_WRONG_VALUE, 206 RETURN_CODE_INVALID, 207 }; 208 const std::string value_check_function = base::StringPrintf( 209 "function valueCheck() {" 210 " var element = document.getElementById('%s');" 211 " return element && element.value == '%s';" 212 "}", 213 element_id.c_str(), 214 expected_value.c_str()); 215 const std::string script = 216 value_check_function + 217 base::StringPrintf( 218 "if (valueCheck()) {" 219 " /* Spin the event loop with setTimeout. */" 220 " setTimeout(window.domAutomationController.send(%d), 0);" 221 "} else {" 222 " var element = document.getElementById('%s');" 223 " if (!element)" 224 " window.domAutomationController.send(%d);" 225 " element.onchange = function() {" 226 " if (valueCheck()) {" 227 " /* Spin the event loop with setTimeout. */" 228 " setTimeout(window.domAutomationController.send(%d), 0);" 229 " } else {" 230 " window.domAutomationController.send(%d);" 231 " }" 232 " };" 233 "}", 234 RETURN_CODE_OK, 235 element_id.c_str(), 236 RETURN_CODE_NO_ELEMENT, 237 RETURN_CODE_OK, 238 RETURN_CODE_WRONG_VALUE); 239 int return_value = RETURN_CODE_INVALID; 240 ASSERT_TRUE(content::ExecuteScriptAndExtractInt( 241 RenderViewHost(), script, &return_value)); 242 EXPECT_EQ(RETURN_CODE_OK, return_value) 243 << "element_id = " << element_id 244 << ", expected_value = " << expected_value; 245 } 246 247 void PasswordManagerBrowserTest::CheckElementValue( 248 const std::string& element_id, 249 const std::string& expected_value) { 250 const std::string value_check_script = base::StringPrintf( 251 "var element = document.getElementById('%s');" 252 "window.domAutomationController.send(element && element.value == '%s');", 253 element_id.c_str(), 254 expected_value.c_str()); 255 bool return_value = false; 256 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 257 RenderViewHost(), value_check_script, &return_value)); 258 EXPECT_TRUE(return_value) << "element_id = " << element_id 259 << ", expected_value = " << expected_value; 260 } 261 262 // Actual tests --------------------------------------------------------------- 263 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 264 PromptForNormalSubmit) { 265 NavigateToFile("/password/password_form.html"); 266 267 // Fill a form and submit through a <input type="submit"> button. Nothing 268 // special. 269 NavigationObserver observer(WebContents()); 270 std::string fill_and_submit = 271 "document.getElementById('username_field').value = 'temp';" 272 "document.getElementById('password_field').value = 'random';" 273 "document.getElementById('input_submit_button').click()"; 274 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 275 observer.Wait(); 276 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 277 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 278 } else { 279 EXPECT_TRUE(observer.infobar_shown()); 280 } 281 } 282 283 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 284 PromptForSubmitWithInPageNavigation) { 285 NavigateToFile("/password/password_navigate_before_submit.html"); 286 287 // Fill a form and submit through a <input type="submit"> button. Nothing 288 // special. The form does an in-page navigation before submitting. 289 NavigationObserver observer(WebContents()); 290 std::string fill_and_submit = 291 "document.getElementById('username_field').value = 'temp';" 292 "document.getElementById('password_field').value = 'random';" 293 "document.getElementById('input_submit_button').click()"; 294 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 295 observer.Wait(); 296 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 297 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 298 } else { 299 EXPECT_TRUE(observer.infobar_shown()); 300 } 301 } 302 303 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 304 LoginSuccessWithUnrelatedForm) { 305 // Log in, see a form on the landing page. That form is not related to the 306 // login form (=has a different action), so we should offer saving the 307 // password. 308 NavigateToFile("/password/password_form.html"); 309 310 NavigationObserver observer(WebContents()); 311 std::string fill_and_submit = 312 "document.getElementById('username_unrelated').value = 'temp';" 313 "document.getElementById('password_unrelated').value = 'random';" 314 "document.getElementById('submit_unrelated').click()"; 315 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 316 observer.Wait(); 317 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 318 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 319 } else { 320 EXPECT_TRUE(observer.infobar_shown()); 321 } 322 } 323 324 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, LoginFailed) { 325 // Log in, see a form on the landing page. That form is not related to the 326 // login form (=has a different action), so we should offer saving the 327 // password. 328 NavigateToFile("/password/password_form.html"); 329 330 NavigationObserver observer(WebContents()); 331 std::string fill_and_submit = 332 "document.getElementById('username_failed').value = 'temp';" 333 "document.getElementById('password_failed').value = 'random';" 334 "document.getElementById('submit_failed').click()"; 335 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 336 observer.Wait(); 337 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 338 EXPECT_FALSE(controller()->PasswordPendingUserDecision()); 339 } else { 340 EXPECT_FALSE(observer.infobar_shown()); 341 } 342 } 343 344 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, Redirects) { 345 NavigateToFile("/password/password_form.html"); 346 347 // Fill a form and submit through a <input type="submit"> button. The form 348 // points to a redirection page. 349 NavigationObserver observer(WebContents()); 350 std::string fill_and_submit = 351 "document.getElementById('username_redirect').value = 'temp';" 352 "document.getElementById('password_redirect').value = 'random';" 353 "document.getElementById('submit_redirect').click()"; 354 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 355 observer.disable_should_automatically_accept_infobar(); 356 observer.Wait(); 357 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 358 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 359 } else { 360 EXPECT_TRUE(observer.infobar_shown()); 361 } 362 363 // The redirection page now redirects via Javascript. We check that the 364 // infobar stays. 365 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), 366 "window.location.href = 'done.html';")); 367 observer.Wait(); 368 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 369 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 370 } else { 371 EXPECT_FALSE(observer.infobar_removed()); 372 } 373 } 374 375 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 376 PromptForSubmitUsingJavaScript) { 377 NavigateToFile("/password/password_form.html"); 378 379 // Fill a form and submit using <button> that calls submit() on the form. 380 // This should work regardless of the type of element, as long as submit() is 381 // called. 382 NavigationObserver observer(WebContents()); 383 std::string fill_and_submit = 384 "document.getElementById('username_field').value = 'temp';" 385 "document.getElementById('password_field').value = 'random';" 386 "document.getElementById('submit_button').click()"; 387 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 388 observer.Wait(); 389 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 390 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 391 } else { 392 EXPECT_TRUE(observer.infobar_shown()); 393 } 394 } 395 396 // Flaky: crbug.com/301547, observed on win and mac. Probably happens on all 397 // platforms. 398 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 399 DISABLED_PromptForDynamicForm) { 400 NavigateToFile("/password/dynamic_password_form.html"); 401 402 // Fill the dynamic password form and submit. 403 NavigationObserver observer(WebContents()); 404 std::string fill_and_submit = 405 "document.getElementById('create_form_button').click();" 406 "window.setTimeout(function() {" 407 " document.dynamic_form.username.value = 'tempro';" 408 " document.dynamic_form.password.value = 'random';" 409 " document.dynamic_form.submit();" 410 "}, 0)"; 411 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 412 observer.Wait(); 413 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 414 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 415 } else { 416 EXPECT_TRUE(observer.infobar_shown()); 417 } 418 } 419 420 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForNavigation) { 421 NavigateToFile("/password/password_form.html"); 422 423 // Don't fill the password form, just navigate away. Shouldn't prompt. 424 NavigationObserver observer(WebContents()); 425 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), 426 "window.location.href = 'done.html';")); 427 observer.Wait(); 428 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 429 EXPECT_FALSE(controller()->PasswordPendingUserDecision()); 430 } else { 431 EXPECT_FALSE(observer.infobar_shown()); 432 } 433 } 434 435 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 436 NoPromptForSubFrameNavigation) { 437 NavigateToFile("/password/multi_frames.html"); 438 439 // If you are filling out a password form in one frame and a different frame 440 // navigates, this should not trigger the infobar. 441 NavigationObserver observer(WebContents()); 442 observer.SetPathToWaitFor("/password/done.html"); 443 std::string fill = 444 "var first_frame = document.getElementById('first_frame');" 445 "var frame_doc = first_frame.contentDocument;" 446 "frame_doc.getElementById('username_field').value = 'temp';" 447 "frame_doc.getElementById('password_field').value = 'random';"; 448 std::string navigate_frame = 449 "var second_iframe = document.getElementById('second_frame');" 450 "second_iframe.contentWindow.location.href = 'done.html';"; 451 452 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill)); 453 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame)); 454 observer.Wait(); 455 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 456 EXPECT_FALSE(controller()->PasswordPendingUserDecision()); 457 } else { 458 EXPECT_FALSE(observer.infobar_shown()); 459 } 460 } 461 462 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 463 PromptAfterSubmitWithSubFrameNavigation) { 464 NavigateToFile("/password/multi_frames.html"); 465 466 // Make sure that we prompt to save password even if a sub-frame navigation 467 // happens first. 468 NavigationObserver observer(WebContents()); 469 observer.SetPathToWaitFor("/password/done.html"); 470 std::string navigate_frame = 471 "var second_iframe = document.getElementById('second_frame');" 472 "second_iframe.contentWindow.location.href = 'other.html';"; 473 std::string fill_and_submit = 474 "var first_frame = document.getElementById('first_frame');" 475 "var frame_doc = first_frame.contentDocument;" 476 "frame_doc.getElementById('username_field').value = 'temp';" 477 "frame_doc.getElementById('password_field').value = 'random';" 478 "frame_doc.getElementById('input_submit_button').click();"; 479 480 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame)); 481 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 482 observer.Wait(); 483 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 484 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 485 } else { 486 EXPECT_TRUE(observer.infobar_shown()); 487 } 488 } 489 490 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 491 PromptForXHRSubmit) { 492 #if defined(OS_WIN) && defined(USE_ASH) 493 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 494 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 495 return; 496 #endif 497 NavigateToFile("/password/password_xhr_submit.html"); 498 499 // Verify that we show the save password prompt if a form returns false 500 // in its onsubmit handler but instead logs in/navigates via XHR. 501 // Note that calling 'submit()' on a form with javascript doesn't call 502 // the onsubmit handler, so we click the submit button instead. 503 NavigationObserver observer(WebContents()); 504 std::string fill_and_submit = 505 "document.getElementById('username_field').value = 'temp';" 506 "document.getElementById('password_field').value = 'random';" 507 "document.getElementById('submit_button').click()"; 508 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 509 observer.Wait(); 510 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 511 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 512 } else { 513 EXPECT_TRUE(observer.infobar_shown()); 514 } 515 } 516 517 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 518 PromptForXHRWithoutOnSubmit) { 519 NavigateToFile("/password/password_xhr_submit.html"); 520 521 // Verify that if XHR navigation occurs and the form is properly filled out, 522 // we try and save the password even though onsubmit hasn't been called. 523 NavigationObserver observer(WebContents()); 524 std::string fill_and_navigate = 525 "document.getElementById('username_field').value = 'temp';" 526 "document.getElementById('password_field').value = 'random';" 527 "send_xhr()"; 528 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate)); 529 observer.Wait(); 530 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 531 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 532 } else { 533 EXPECT_TRUE(observer.infobar_shown()); 534 } 535 } 536 537 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 538 NoPromptIfLinkClicked) { 539 NavigateToFile("/password/password_form.html"); 540 541 // Verify that if the user takes a direct action to leave the page, we don't 542 // prompt to save the password even if the form is already filled out. 543 NavigationObserver observer(WebContents()); 544 std::string fill_and_click_link = 545 "document.getElementById('username_field').value = 'temp';" 546 "document.getElementById('password_field').value = 'random';" 547 "document.getElementById('link').click();"; 548 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_click_link)); 549 observer.Wait(); 550 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 551 EXPECT_FALSE(controller()->PasswordPendingUserDecision()); 552 } else { 553 EXPECT_FALSE(observer.infobar_shown()); 554 } 555 } 556 557 // TODO(jam): http://crbug.com/350550 558 #if !defined(OS_WIN) 559 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 560 VerifyPasswordGenerationUpload) { 561 // Prevent Autofill requests from actually going over the wire. 562 net::TestURLFetcherFactory factory; 563 // Disable Autofill requesting access to AddressBook data. This causes 564 // the test to hang on Mac. 565 autofill::test::DisableSystemServices(browser()->profile()->GetPrefs()); 566 567 // Visit a signup form. 568 NavigateToFile("/password/signup_form.html"); 569 570 // Enter a password and save it. 571 NavigationObserver first_observer(WebContents()); 572 std::string fill_and_submit = 573 "document.getElementById('other_info').value = 'stuff';" 574 "document.getElementById('username_field').value = 'my_username';" 575 "document.getElementById('password_field').value = 'password';" 576 "document.getElementById('input_submit_button').click()"; 577 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 578 579 first_observer.Wait(); 580 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 581 ASSERT_TRUE(controller()->PasswordPendingUserDecision()); 582 controller()->SavePassword(); 583 } else { 584 ASSERT_TRUE(first_observer.infobar_shown()); 585 } 586 587 // Now navigate to a login form that has similar HTML markup. 588 NavigateToFile("/password/password_form.html"); 589 590 // Simulate a user click to force an autofill of the form's DOM value, not 591 // just the suggested value. 592 content::SimulateMouseClick( 593 WebContents(), 0, blink::WebMouseEvent::ButtonLeft); 594 595 // The form should be filled with the previously submitted username. 596 std::string get_username = 597 "window.domAutomationController.send(" 598 "document.getElementById('username_field').value);"; 599 std::string actual_username; 600 ASSERT_TRUE(content::ExecuteScriptAndExtractString(RenderViewHost(), 601 get_username, 602 &actual_username)); 603 ASSERT_EQ("my_username", actual_username); 604 605 // Submit the form and verify that there is no infobar (as the password 606 // has already been saved). 607 NavigationObserver second_observer(WebContents()); 608 std::string submit_form = 609 "document.getElementById('input_submit_button').click()"; 610 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit_form)); 611 second_observer.Wait(); 612 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 613 EXPECT_FALSE(controller()->PasswordPendingUserDecision()); 614 } else { 615 EXPECT_FALSE(second_observer.infobar_shown()); 616 } 617 618 // Verify that we sent a ping to Autofill saying that the original form 619 // was likely an account creation form since it has more than 2 text input 620 // fields and was used for the first time on a different form. 621 base::HistogramBase* upload_histogram = 622 base::StatisticsRecorder::FindHistogram( 623 "PasswordGeneration.UploadStarted"); 624 ASSERT_TRUE(upload_histogram); 625 scoped_ptr<base::HistogramSamples> snapshot = 626 upload_histogram->SnapshotSamples(); 627 EXPECT_EQ(0, snapshot->GetCount(0 /* failure */)); 628 EXPECT_EQ(1, snapshot->GetCount(1 /* success */)); 629 } 630 #endif 631 632 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForSubmitFromIframe) { 633 NavigateToFile("/password/password_submit_from_iframe.html"); 634 635 // Submit a form in an iframe, then cause the whole page to navigate without a 636 // user gesture. We expect the save password prompt to be shown here, because 637 // some pages use such iframes for login forms. 638 NavigationObserver observer(WebContents()); 639 std::string fill_and_submit = 640 "var iframe = document.getElementById('test_iframe');" 641 "var iframe_doc = iframe.contentDocument;" 642 "iframe_doc.getElementById('username_field').value = 'temp';" 643 "iframe_doc.getElementById('password_field').value = 'random';" 644 "iframe_doc.getElementById('submit_button').click()"; 645 646 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 647 observer.Wait(); 648 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 649 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 650 } else { 651 EXPECT_TRUE(observer.infobar_shown()); 652 } 653 } 654 655 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 656 PromptForInputElementWithoutName) { 657 // Check that the prompt is shown for forms where input elements lack the 658 // "name" attribute but the "id" is present. 659 NavigateToFile("/password/password_form.html"); 660 661 NavigationObserver observer(WebContents()); 662 std::string fill_and_submit = 663 "document.getElementById('username_field_no_name').value = 'temp';" 664 "document.getElementById('password_field_no_name').value = 'random';" 665 "document.getElementById('input_submit_button_no_name').click()"; 666 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 667 observer.Wait(); 668 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 669 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 670 } else { 671 EXPECT_TRUE(observer.infobar_shown()); 672 } 673 } 674 675 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 676 PromptForInputElementWithoutId) { 677 // Check that the prompt is shown for forms where input elements lack the 678 // "id" attribute but the "name" attribute is present. 679 NavigateToFile("/password/password_form.html"); 680 681 NavigationObserver observer(WebContents()); 682 std::string fill_and_submit = 683 "document.getElementsByName('username_field_no_id')[0].value = 'temp';" 684 "document.getElementsByName('password_field_no_id')[0].value = 'random';" 685 "document.getElementsByName('input_submit_button_no_id')[0].click()"; 686 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 687 observer.Wait(); 688 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 689 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 690 } else { 691 EXPECT_TRUE(observer.infobar_shown()); 692 } 693 } 694 695 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 696 NoPromptForInputElementWithoutIdAndName) { 697 // Check that no prompt is shown for forms where the input fields lack both 698 // the "id" and the "name" attributes. 699 NavigateToFile("/password/password_form.html"); 700 701 NavigationObserver observer(WebContents()); 702 std::string fill_and_submit = 703 "var form = document.getElementById('testform_elements_no_id_no_name');" 704 "var username = form.children[0];" 705 "username.value = 'temp';" 706 "var password = form.children[1];" 707 "password.value = 'random';" 708 "form.children[2].click()"; // form.children[2] is the submit button. 709 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 710 observer.Wait(); 711 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 712 EXPECT_FALSE(controller()->PasswordPendingUserDecision()); 713 } else { 714 EXPECT_FALSE(observer.infobar_shown()); 715 } 716 } 717 718 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, DeleteFrameBeforeSubmit) { 719 NavigateToFile("/password/multi_frames.html"); 720 721 NavigationObserver observer(WebContents()); 722 // Make sure we save some password info from an iframe and then destroy it. 723 std::string save_and_remove = 724 "var first_frame = document.getElementById('first_frame');" 725 "var frame_doc = first_frame.contentDocument;" 726 "frame_doc.getElementById('username_field').value = 'temp';" 727 "frame_doc.getElementById('password_field').value = 'random';" 728 "frame_doc.getElementById('input_submit_button').click();" 729 "first_frame.parentNode.removeChild(first_frame);"; 730 // Submit from the main frame, but without navigating through the onsubmit 731 // handler. 732 std::string navigate_frame = 733 "document.getElementById('username_field').value = 'temp';" 734 "document.getElementById('password_field').value = 'random';" 735 "document.getElementById('input_submit_button').click();" 736 "window.location.href = 'done.html';"; 737 738 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), save_and_remove)); 739 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame)); 740 observer.Wait(); 741 // The only thing we check here is that there is no use-after-free reported. 742 } 743 744 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PasswordValueAccessible) { 745 NavigateToFile("/password/form_and_link.html"); 746 747 // Click on a link to open a new tab, then switch back to the first one. 748 EXPECT_EQ(1, browser()->tab_strip_model()->count()); 749 std::string click = 750 "document.getElementById('testlink').click();"; 751 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), click)); 752 EXPECT_EQ(2, browser()->tab_strip_model()->count()); 753 browser()->tab_strip_model()->ActivateTabAt(0, false); 754 755 // Fill in the credentials, and make sure they are saved. 756 NavigationObserver form_submit_observer(WebContents()); 757 std::string fill_and_submit = 758 "document.getElementById('username_field').value = 'temp';" 759 "document.getElementById('password_field').value = 'random';" 760 "document.getElementById('input_submit_button').click();"; 761 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 762 form_submit_observer.Wait(); 763 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 764 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 765 controller()->SavePassword(); 766 } else { 767 EXPECT_TRUE(form_submit_observer.infobar_shown()); 768 } 769 770 // Reload the original page to have the saved credentials autofilled. 771 NavigationObserver reload_observer(WebContents()); 772 NavigateToFile("/password/form_and_link.html"); 773 reload_observer.Wait(); 774 775 // Wait until the username is filled, to make sure autofill kicked in. 776 WaitForElementValue("username_field", "temp"); 777 // Now check that the password is not accessible yet. 778 CheckElementValue("password_field", ""); 779 // Let the user interact with the page. 780 content::SimulateMouseClickAt( 781 WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(1, 1)); 782 // Wait until that interaction causes the password value to be revealed. 783 WaitForElementValue("password_field", "random"); 784 // And check that after the side-effects of the interaction took place, the 785 // username value stays the same. 786 CheckElementValue("username_field", "temp"); 787 } 788 789 // The following test is limited to Aura, because 790 // RenderWidgetHostViewGuest::ProcessAckedTouchEvent is, and 791 // ProcessAckedTouchEvent is what triggers the translation of touch events to 792 // gesture events. 793 #if defined(USE_AURA) 794 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 795 PasswordValueAccessibleOnSubmit) { 796 NavigateToFile("/password/form_and_link.html"); 797 798 // Fill in the credentials, and make sure they are saved. 799 NavigationObserver form_submit_observer(WebContents()); 800 std::string fill_and_submit = 801 "document.getElementById('username_field').value = 'temp';" 802 "document.getElementById('password_field').value = 'random_secret';" 803 "document.getElementById('input_submit_button').click();"; 804 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 805 form_submit_observer.Wait(); 806 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 807 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 808 controller()->SavePassword(); 809 } else { 810 EXPECT_TRUE(form_submit_observer.infobar_shown()); 811 } 812 813 // Reload the original page to have the saved credentials autofilled. 814 NavigationObserver reload_observer(WebContents()); 815 NavigateToFile("/password/form_and_link.html"); 816 reload_observer.Wait(); 817 818 NavigationObserver submit_observer(WebContents()); 819 // Submit the form via a tap on the submit button. The button is placed at 0, 820 // 100, and has height 300 and width 700. 821 content::SimulateTapAt(WebContents(), gfx::Point(350, 250)); 822 submit_observer.Wait(); 823 std::string query = WebContents()->GetURL().query(); 824 EXPECT_NE(std::string::npos, query.find("random_secret")) << query; 825 } 826 #endif 827 828 // Test fix for crbug.com/338650. 829 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 830 DontPromptForPasswordFormWithDefaultValue) { 831 NavigateToFile("/password/password_form_with_default_value.html"); 832 833 // Don't prompt if we navigate away even if there is a password value since 834 // it's not coming from the user. 835 NavigationObserver observer(WebContents()); 836 NavigateToFile("/password/done.html"); 837 observer.Wait(); 838 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 839 EXPECT_FALSE(controller()->PasswordPendingUserDecision()); 840 } else { 841 EXPECT_FALSE(observer.infobar_shown()); 842 } 843 } 844 845 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 846 PromptWhenEnableAutomaticPasswordSavingSwitchIsNotSet) { 847 NavigateToFile("/password/password_form.html"); 848 849 // Fill a form and submit through a <input type="submit"> button. 850 NavigationObserver observer(WebContents()); 851 std::string fill_and_submit = 852 "document.getElementById('username_field').value = 'temp';" 853 "document.getElementById('password_field').value = 'random';" 854 "document.getElementById('input_submit_button').click()"; 855 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 856 observer.Wait(); 857 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 858 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 859 } else { 860 EXPECT_TRUE(observer.infobar_shown()); 861 } 862 } 863 864 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 865 DontPromptWhenEnableAutomaticPasswordSavingSwitchIsSet) { 866 password_manager::TestPasswordStore* password_store = 867 static_cast<password_manager::TestPasswordStore*>( 868 PasswordStoreFactory::GetForProfile(browser()->profile(), 869 Profile::IMPLICIT_ACCESS).get()); 870 871 EXPECT_TRUE(password_store->IsEmpty()); 872 873 NavigateToFile("/password/password_form.html"); 874 875 // Add the enable-automatic-password-saving switch. 876 CommandLine::ForCurrentProcess()->AppendSwitch( 877 password_manager::switches::kEnableAutomaticPasswordSaving); 878 879 // Fill a form and submit through a <input type="submit"> button. 880 NavigationObserver observer(WebContents()); 881 // Make sure that the only passwords saved are the auto-saved ones. 882 observer.disable_should_automatically_accept_infobar(); 883 std::string fill_and_submit = 884 "document.getElementById('username_field').value = 'temp';" 885 "document.getElementById('password_field').value = 'random';" 886 "document.getElementById('input_submit_button').click()"; 887 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 888 observer.Wait(); 889 if (chrome::VersionInfo::GetChannel() == 890 chrome::VersionInfo::CHANNEL_UNKNOWN) { 891 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 892 EXPECT_FALSE(controller()->PasswordPendingUserDecision()); 893 } else { 894 EXPECT_FALSE(observer.infobar_shown()); 895 } 896 EXPECT_FALSE(password_store->IsEmpty()); 897 } else { 898 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 899 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 900 } else { 901 EXPECT_TRUE(observer.infobar_shown()); 902 } 903 EXPECT_TRUE(password_store->IsEmpty()); 904 } 905 } 906 907 // Test fix for crbug.com/368690. 908 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptWhenReloading) { 909 NavigateToFile("/password/password_form.html"); 910 911 std::string fill = 912 "document.getElementById('username_redirect').value = 'temp';" 913 "document.getElementById('password_redirect').value = 'random';"; 914 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill)); 915 916 NavigationObserver observer(WebContents()); 917 GURL url = embedded_test_server()->GetURL("/password/password_form.html"); 918 chrome::NavigateParams params(browser(), url, 919 content::PAGE_TRANSITION_RELOAD); 920 ui_test_utils::NavigateToURL(¶ms); 921 observer.Wait(); 922 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 923 EXPECT_FALSE(controller()->PasswordPendingUserDecision()); 924 } else { 925 EXPECT_FALSE(observer.infobar_shown()); 926 } 927 } 928 929 // Test that if a form gets dynamically added between the form parsing and 930 // rendering, and while the main frame still loads, it still is registered, and 931 // thus saving passwords from it works. 932 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 933 FormsAddedBetweenParsingAndRendering) { 934 NavigateToFile("/password/between_parsing_and_rendering.html"); 935 936 NavigationObserver observer(WebContents()); 937 std::string submit = 938 "document.getElementById('username').value = 'temp';" 939 "document.getElementById('password').value = 'random';" 940 "document.getElementById('submit-button').click();"; 941 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit)); 942 observer.Wait(); 943 944 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 945 EXPECT_TRUE(controller()->PasswordPendingUserDecision()); 946 } else { 947 EXPECT_TRUE(observer.infobar_shown()); 948 } 949 } 950