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/path_service.h" 11#include "base/run_loop.h" 12#include "base/stl_util.h" 13#include "base/strings/stringprintf.h" 14#include "base/strings/utf_string_conversions.h" 15#include "chrome/browser/chrome_notification_types.h" 16#include "chrome/browser/infobars/infobar_service.h" 17#include "chrome/browser/password_manager/chrome_password_manager_client.h" 18#include "chrome/browser/password_manager/password_store_factory.h" 19#include "chrome/browser/password_manager/test_password_store_service.h" 20#include "chrome/browser/ui/browser.h" 21#include "chrome/browser/ui/login/login_prompt.h" 22#include "chrome/browser/ui/login/login_prompt_test_utils.h" 23#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h" 24#include "chrome/browser/ui/tabs/tab_strip_model.h" 25#include "chrome/common/chrome_paths.h" 26#include "chrome/common/chrome_switches.h" 27#include "chrome/common/chrome_version_info.h" 28#include "chrome/test/base/in_process_browser_test.h" 29#include "chrome/test/base/test_switches.h" 30#include "chrome/test/base/ui_test_utils.h" 31#include "components/autofill/core/browser/autofill_test_utils.h" 32#include "components/infobars/core/confirm_infobar_delegate.h" 33#include "components/infobars/core/infobar.h" 34#include "components/infobars/core/infobar_manager.h" 35#include "components/password_manager/core/browser/test_password_store.h" 36#include "components/password_manager/core/common/password_manager_switches.h" 37#include "content/public/browser/navigation_controller.h" 38#include "content/public/browser/notification_service.h" 39#include "content/public/browser/render_frame_host.h" 40#include "content/public/browser/render_view_host.h" 41#include "content/public/browser/web_contents.h" 42#include "content/public/browser/web_contents_observer.h" 43#include "content/public/common/content_switches.h" 44#include "content/public/test/browser_test_utils.h" 45#include "content/public/test/test_utils.h" 46#include "net/base/filename_util.h" 47#include "net/test/embedded_test_server/embedded_test_server.h" 48#include "net/test/embedded_test_server/http_request.h" 49#include "net/test/embedded_test_server/http_response.h" 50#include "net/test/spawned_test_server/spawned_test_server.h" 51#include "net/url_request/test_url_fetcher_factory.h" 52#include "testing/gmock/include/gmock/gmock.h" 53#include "third_party/WebKit/public/web/WebInputEvent.h" 54#include "ui/events/keycodes/keyboard_codes.h" 55#include "ui/gfx/geometry/point.h" 56 57 58// NavigationObserver --------------------------------------------------------- 59 60namespace { 61 62// Observer that waits for navigation to complete and for the password infobar 63// to be shown. 64class NavigationObserver : public content::WebContentsObserver { 65 public: 66 explicit NavigationObserver(content::WebContents* web_contents) 67 : content::WebContentsObserver(web_contents), 68 message_loop_runner_(new content::MessageLoopRunner) {} 69 70 virtual ~NavigationObserver() {} 71 72 // Normally Wait() will not return until a main frame navigation occurs. 73 // If a path is set, Wait() will return after this path has been seen, 74 // regardless of the frame that navigated. Useful for multi-frame pages. 75 void SetPathToWaitFor(const std::string& path) { 76 wait_for_path_ = path; 77 } 78 79 // content::WebContentsObserver: 80 virtual void DidFinishLoad(content::RenderFrameHost* render_frame_host, 81 const GURL& validated_url) OVERRIDE { 82 if (!wait_for_path_.empty()) { 83 if (validated_url.path() == wait_for_path_) 84 message_loop_runner_->Quit(); 85 } else if (!render_frame_host->GetParent()) { 86 message_loop_runner_->Quit(); 87 } 88 } 89 90 void Wait() { message_loop_runner_->Run(); } 91 92 private: 93 std::string wait_for_path_; 94 scoped_refptr<content::MessageLoopRunner> message_loop_runner_; 95 96 DISALLOW_COPY_AND_ASSIGN(NavigationObserver); 97}; 98 99// Observes the save password prompt (bubble or infobar) for a specified 100// WebContents, keeps track of whether or not it is currently shown, and allows 101// accepting saving passwords through it. 102class PromptObserver { 103 public: 104 virtual ~PromptObserver() {} 105 106 // Checks if the prompt is being currently shown. 107 virtual bool IsShowingPrompt() const = 0; 108 109 // Expecting that the prompt is shown, saves the password. Checks that the 110 // prompt is no longer visible afterwards. 111 void Accept() const { 112 EXPECT_TRUE(IsShowingPrompt()); 113 AcceptImpl(); 114 } 115 116 // Chooses the right implementation of PromptObserver and creates an instance 117 // of it. 118 static scoped_ptr<PromptObserver> Create(content::WebContents* web_contents); 119 120 protected: 121 PromptObserver() {} 122 123 // Accepts the password. The implementation can assume that the prompt is 124 // currently shown, but is required to verify that the prompt is eventually 125 // closed. 126 virtual void AcceptImpl() const = 0; 127 128 private: 129 DISALLOW_COPY_AND_ASSIGN(PromptObserver); 130}; 131 132class InfoBarObserver : public PromptObserver, 133 public infobars::InfoBarManager::Observer { 134 public: 135 explicit InfoBarObserver(content::WebContents* web_contents) 136 : infobar_is_being_shown_(false), 137 infobar_service_(InfoBarService::FromWebContents(web_contents)) { 138 infobar_service_->AddObserver(this); 139 } 140 141 virtual ~InfoBarObserver() { 142 if (infobar_service_) 143 infobar_service_->RemoveObserver(this); 144 } 145 146 private: 147 // PromptObserver: 148 virtual bool IsShowingPrompt() const OVERRIDE { 149 return infobar_is_being_shown_; 150 } 151 152 virtual void AcceptImpl() const OVERRIDE { 153 EXPECT_EQ(1u, infobar_service_->infobar_count()); 154 if (!infobar_service_->infobar_count()) 155 return; // Let the test finish to gather possibly more diagnostics. 156 157 // ConfirmInfoBarDelegate::Accept returning true means the infobar is 158 // immediately closed. Checking the return value is preferred to testing 159 // IsShowingPrompt() here, for it avoids the delay until the closing 160 // notification is received. 161 EXPECT_TRUE(infobar_service_->infobar_at(0) 162 ->delegate() 163 ->AsConfirmInfoBarDelegate() 164 ->Accept()); 165 } 166 167 // infobars::InfoBarManager::Observer: 168 virtual void OnInfoBarAdded(infobars::InfoBar* infobar) OVERRIDE { 169 infobar_is_being_shown_ = true; 170 } 171 172 virtual void OnInfoBarRemoved(infobars::InfoBar* infobar, 173 bool animate) OVERRIDE { 174 infobar_is_being_shown_ = false; 175 } 176 177 virtual void OnManagerShuttingDown( 178 infobars::InfoBarManager* manager) OVERRIDE { 179 ASSERT_EQ(infobar_service_, manager); 180 infobar_service_->RemoveObserver(this); 181 infobar_service_ = NULL; 182 } 183 184 bool infobar_is_being_shown_; 185 InfoBarService* infobar_service_; 186 187 DISALLOW_COPY_AND_ASSIGN(InfoBarObserver); 188}; 189 190class BubbleObserver : public PromptObserver { 191 public: 192 explicit BubbleObserver(content::WebContents* web_contents) 193 : ui_controller_( 194 ManagePasswordsUIController::FromWebContents(web_contents)) {} 195 196 virtual ~BubbleObserver() {} 197 198 private: 199 // PromptObserver: 200 virtual bool IsShowingPrompt() const OVERRIDE { 201 return ui_controller_->PasswordPendingUserDecision(); 202 } 203 204 virtual void AcceptImpl() const OVERRIDE { 205 ui_controller_->SavePassword(); 206 EXPECT_FALSE(IsShowingPrompt()); 207 } 208 209 ManagePasswordsUIController* const ui_controller_; 210 211 DISALLOW_COPY_AND_ASSIGN(BubbleObserver); 212}; 213 214GURL GetFileURL(const char* filename) { 215 base::FilePath path; 216 PathService::Get(chrome::DIR_TEST_DATA, &path); 217 path = path.AppendASCII("password").AppendASCII(filename); 218 CHECK(base::PathExists(path)); 219 return net::FilePathToFileURL(path); 220} 221 222// static 223scoped_ptr<PromptObserver> PromptObserver::Create( 224 content::WebContents* web_contents) { 225 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { 226 return scoped_ptr<PromptObserver>(new BubbleObserver(web_contents)); 227 } else { 228 return scoped_ptr<PromptObserver>(new InfoBarObserver(web_contents)); 229 } 230} 231 232// Handles |request| to "/basic_auth". If "Authorization" header is present, 233// responds with a non-empty HTTP 200 page (regardless of its value). Otherwise 234// serves a Basic Auth challenge. 235scoped_ptr<net::test_server::HttpResponse> HandleTestAuthRequest( 236 const net::test_server::HttpRequest& request) { 237 if (!StartsWithASCII(request.relative_url, "/basic_auth", true)) 238 return scoped_ptr<net::test_server::HttpResponse>(); 239 240 if (ContainsKey(request.headers, "Authorization")) { 241 scoped_ptr<net::test_server::BasicHttpResponse> http_response( 242 new net::test_server::BasicHttpResponse); 243 http_response->set_code(net::HTTP_OK); 244 http_response->set_content("Success!"); 245 return http_response.PassAs<net::test_server::HttpResponse>(); 246 } else { 247 scoped_ptr<net::test_server::BasicHttpResponse> http_response( 248 new net::test_server::BasicHttpResponse); 249 http_response->set_code(net::HTTP_UNAUTHORIZED); 250 http_response->AddCustomHeader("WWW-Authenticate", 251 "Basic realm=\"test realm\""); 252 return http_response.PassAs<net::test_server::HttpResponse>(); 253 } 254} 255 256} // namespace 257 258 259// PasswordManagerBrowserTest ------------------------------------------------- 260 261class PasswordManagerBrowserTest : public InProcessBrowserTest { 262 public: 263 PasswordManagerBrowserTest() {} 264 virtual ~PasswordManagerBrowserTest() {} 265 266 // InProcessBrowserTest: 267 virtual void SetUpOnMainThread() OVERRIDE { 268 // Use TestPasswordStore to remove a possible race. Normally the 269 // PasswordStore does its database manipulation on the DB thread, which 270 // creates a possible race during navigation. Specifically the 271 // PasswordManager will ignore any forms in a page if the load from the 272 // PasswordStore has not completed. 273 PasswordStoreFactory::GetInstance()->SetTestingFactory( 274 browser()->profile(), TestPasswordStoreService::Build); 275 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 276 ASSERT_FALSE(CommandLine::ForCurrentProcess()->HasSwitch( 277 password_manager::switches::kEnableAutomaticPasswordSaving)); 278 } 279 280 virtual void TearDownOnMainThread() OVERRIDE { 281 ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); 282 } 283 284 protected: 285 content::WebContents* WebContents() { 286 return browser()->tab_strip_model()->GetActiveWebContents(); 287 } 288 289 content::RenderViewHost* RenderViewHost() { 290 return WebContents()->GetRenderViewHost(); 291 } 292 293 // Wrapper around ui_test_utils::NavigateToURL that waits until 294 // DidFinishLoad() fires. Normally this function returns after 295 // DidStopLoading(), which caused flakiness as the NavigationObserver 296 // would sometimes see the DidFinishLoad event from a previous navigation and 297 // return immediately. 298 void NavigateToFile(const std::string& path) { 299 NavigationObserver observer(WebContents()); 300 GURL url = embedded_test_server()->GetURL(path); 301 ui_test_utils::NavigateToURL(browser(), url); 302 observer.Wait(); 303 } 304 305 // Waits until the "value" attribute of the HTML element with |element_id| is 306 // equal to |expected_value|. If the current value is not as expected, this 307 // waits until the "change" event is fired for the element. This also 308 // guarantees that once the real value matches the expected, the JavaScript 309 // event loop is spun to allow all other possible events to take place. 310 void WaitForElementValue(const std::string& element_id, 311 const std::string& expected_value); 312 // Checks that the current "value" attribute of the HTML element with 313 // |element_id| is equal to |expected_value|. 314 void CheckElementValue(const std::string& element_id, 315 const std::string& expected_value); 316 317 private: 318 DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest); 319}; 320 321void PasswordManagerBrowserTest::WaitForElementValue( 322 const std::string& element_id, 323 const std::string& expected_value) { 324 enum ReturnCodes { // Possible results of the JavaScript code. 325 RETURN_CODE_OK, 326 RETURN_CODE_NO_ELEMENT, 327 RETURN_CODE_WRONG_VALUE, 328 RETURN_CODE_INVALID, 329 }; 330 const std::string value_check_function = base::StringPrintf( 331 "function valueCheck() {" 332 " var element = document.getElementById('%s');" 333 " return element && element.value == '%s';" 334 "}", 335 element_id.c_str(), 336 expected_value.c_str()); 337 const std::string script = 338 value_check_function + 339 base::StringPrintf( 340 "if (valueCheck()) {" 341 " /* Spin the event loop with setTimeout. */" 342 " setTimeout(window.domAutomationController.send(%d), 0);" 343 "} else {" 344 " var element = document.getElementById('%s');" 345 " if (!element)" 346 " window.domAutomationController.send(%d);" 347 " element.onchange = function() {" 348 " if (valueCheck()) {" 349 " /* Spin the event loop with setTimeout. */" 350 " setTimeout(window.domAutomationController.send(%d), 0);" 351 " } else {" 352 " window.domAutomationController.send(%d);" 353 " }" 354 " };" 355 "}", 356 RETURN_CODE_OK, 357 element_id.c_str(), 358 RETURN_CODE_NO_ELEMENT, 359 RETURN_CODE_OK, 360 RETURN_CODE_WRONG_VALUE); 361 int return_value = RETURN_CODE_INVALID; 362 ASSERT_TRUE(content::ExecuteScriptAndExtractInt( 363 RenderViewHost(), script, &return_value)); 364 EXPECT_EQ(RETURN_CODE_OK, return_value) 365 << "element_id = " << element_id 366 << ", expected_value = " << expected_value; 367} 368 369void PasswordManagerBrowserTest::CheckElementValue( 370 const std::string& element_id, 371 const std::string& expected_value) { 372 const std::string value_check_script = base::StringPrintf( 373 "var element = document.getElementById('%s');" 374 "window.domAutomationController.send(element && element.value == '%s');", 375 element_id.c_str(), 376 expected_value.c_str()); 377 bool return_value = false; 378 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 379 RenderViewHost(), value_check_script, &return_value)); 380 EXPECT_TRUE(return_value) << "element_id = " << element_id 381 << ", expected_value = " << expected_value; 382} 383 384// Actual tests --------------------------------------------------------------- 385IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 386 PromptForNormalSubmit) { 387 NavigateToFile("/password/password_form.html"); 388 389 // Fill a form and submit through a <input type="submit"> button. Nothing 390 // special. 391 NavigationObserver observer(WebContents()); 392 scoped_ptr<PromptObserver> prompt_observer( 393 PromptObserver::Create(WebContents())); 394 std::string fill_and_submit = 395 "document.getElementById('username_field').value = 'temp';" 396 "document.getElementById('password_field').value = 'random';" 397 "document.getElementById('input_submit_button').click()"; 398 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 399 observer.Wait(); 400 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 401} 402 403IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 404 PromptForSubmitWithInPageNavigation) { 405 NavigateToFile("/password/password_navigate_before_submit.html"); 406 407 // Fill a form and submit through a <input type="submit"> button. Nothing 408 // special. The form does an in-page navigation before submitting. 409 NavigationObserver observer(WebContents()); 410 scoped_ptr<PromptObserver> prompt_observer( 411 PromptObserver::Create(WebContents())); 412 std::string fill_and_submit = 413 "document.getElementById('username_field').value = 'temp';" 414 "document.getElementById('password_field').value = 'random';" 415 "document.getElementById('input_submit_button').click()"; 416 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 417 observer.Wait(); 418 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 419} 420 421IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 422 LoginSuccessWithUnrelatedForm) { 423 // Log in, see a form on the landing page. That form is not related to the 424 // login form (=has a different action), so we should offer saving the 425 // password. 426 NavigateToFile("/password/password_form.html"); 427 428 NavigationObserver observer(WebContents()); 429 scoped_ptr<PromptObserver> prompt_observer( 430 PromptObserver::Create(WebContents())); 431 std::string fill_and_submit = 432 "document.getElementById('username_unrelated').value = 'temp';" 433 "document.getElementById('password_unrelated').value = 'random';" 434 "document.getElementById('submit_unrelated').click()"; 435 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 436 observer.Wait(); 437 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 438} 439 440IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, LoginFailed) { 441 // Log in, see a form on the landing page. That form is not related to the 442 // login form (=has a different action), so we should offer saving the 443 // password. 444 NavigateToFile("/password/password_form.html"); 445 446 NavigationObserver observer(WebContents()); 447 scoped_ptr<PromptObserver> prompt_observer( 448 PromptObserver::Create(WebContents())); 449 std::string fill_and_submit = 450 "document.getElementById('username_failed').value = 'temp';" 451 "document.getElementById('password_failed').value = 'random';" 452 "document.getElementById('submit_failed').click()"; 453 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 454 observer.Wait(); 455 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 456} 457 458IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, Redirects) { 459 NavigateToFile("/password/password_form.html"); 460 461 // Fill a form and submit through a <input type="submit"> button. The form 462 // points to a redirection page. 463 NavigationObserver observer(WebContents()); 464 scoped_ptr<PromptObserver> prompt_observer( 465 PromptObserver::Create(WebContents())); 466 std::string fill_and_submit = 467 "document.getElementById('username_redirect').value = 'temp';" 468 "document.getElementById('password_redirect').value = 'random';" 469 "document.getElementById('submit_redirect').click()"; 470 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 471 observer.Wait(); 472 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 473 474 // The redirection page now redirects via Javascript. We check that the 475 // infobar stays. 476 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), 477 "window.location.href = 'done.html';")); 478 observer.Wait(); 479 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 480} 481 482IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 483 PromptForSubmitUsingJavaScript) { 484 NavigateToFile("/password/password_form.html"); 485 486 // Fill a form and submit using <button> that calls submit() on the form. 487 // This should work regardless of the type of element, as long as submit() is 488 // called. 489 NavigationObserver observer(WebContents()); 490 scoped_ptr<PromptObserver> prompt_observer( 491 PromptObserver::Create(WebContents())); 492 std::string fill_and_submit = 493 "document.getElementById('username_field').value = 'temp';" 494 "document.getElementById('password_field').value = 'random';" 495 "document.getElementById('submit_button').click()"; 496 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 497 observer.Wait(); 498 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 499} 500 501// Flaky: crbug.com/301547, observed on win and mac. Probably happens on all 502// platforms. 503IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 504 DISABLED_PromptForDynamicForm) { 505 NavigateToFile("/password/dynamic_password_form.html"); 506 507 // Fill the dynamic password form and submit. 508 NavigationObserver observer(WebContents()); 509 scoped_ptr<PromptObserver> prompt_observer( 510 PromptObserver::Create(WebContents())); 511 std::string fill_and_submit = 512 "document.getElementById('create_form_button').click();" 513 "window.setTimeout(function() {" 514 " document.dynamic_form.username.value = 'tempro';" 515 " document.dynamic_form.password.value = 'random';" 516 " document.dynamic_form.submit();" 517 "}, 0)"; 518 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 519 observer.Wait(); 520 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 521} 522 523IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForNavigation) { 524 NavigateToFile("/password/password_form.html"); 525 526 // Don't fill the password form, just navigate away. Shouldn't prompt. 527 NavigationObserver observer(WebContents()); 528 scoped_ptr<PromptObserver> prompt_observer( 529 PromptObserver::Create(WebContents())); 530 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), 531 "window.location.href = 'done.html';")); 532 observer.Wait(); 533 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 534} 535 536IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 537 NoPromptForSubFrameNavigation) { 538 NavigateToFile("/password/multi_frames.html"); 539 540 // If you are filling out a password form in one frame and a different frame 541 // navigates, this should not trigger the infobar. 542 NavigationObserver observer(WebContents()); 543 scoped_ptr<PromptObserver> prompt_observer( 544 PromptObserver::Create(WebContents())); 545 observer.SetPathToWaitFor("/password/done.html"); 546 std::string fill = 547 "var first_frame = document.getElementById('first_frame');" 548 "var frame_doc = first_frame.contentDocument;" 549 "frame_doc.getElementById('username_field').value = 'temp';" 550 "frame_doc.getElementById('password_field').value = 'random';"; 551 std::string navigate_frame = 552 "var second_iframe = document.getElementById('second_frame');" 553 "second_iframe.contentWindow.location.href = 'done.html';"; 554 555 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill)); 556 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame)); 557 observer.Wait(); 558 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 559} 560 561IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 562 PromptAfterSubmitWithSubFrameNavigation) { 563 NavigateToFile("/password/multi_frames.html"); 564 565 // Make sure that we prompt to save password even if a sub-frame navigation 566 // happens first. 567 NavigationObserver observer(WebContents()); 568 scoped_ptr<PromptObserver> prompt_observer( 569 PromptObserver::Create(WebContents())); 570 observer.SetPathToWaitFor("/password/done.html"); 571 std::string navigate_frame = 572 "var second_iframe = document.getElementById('second_frame');" 573 "second_iframe.contentWindow.location.href = 'other.html';"; 574 std::string fill_and_submit = 575 "var first_frame = document.getElementById('first_frame');" 576 "var frame_doc = first_frame.contentDocument;" 577 "frame_doc.getElementById('username_field').value = 'temp';" 578 "frame_doc.getElementById('password_field').value = 'random';" 579 "frame_doc.getElementById('input_submit_button').click();"; 580 581 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame)); 582 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 583 observer.Wait(); 584 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 585} 586 587IN_PROC_BROWSER_TEST_F( 588 PasswordManagerBrowserTest, 589 NoPromptForFailedLoginFromMainFrameWithMultiFramesInPage) { 590 NavigateToFile("/password/multi_frames.html"); 591 592 // Make sure that we don't prompt to save the password for a failed login 593 // from the main frame with multiple frames in the same page. 594 NavigationObserver observer(WebContents()); 595 scoped_ptr<PromptObserver> prompt_observer( 596 PromptObserver::Create(WebContents())); 597 std::string fill_and_submit = 598 "document.getElementById('username_failed').value = 'temp';" 599 "document.getElementById('password_failed').value = 'random';" 600 "document.getElementById('submit_failed').click();"; 601 602 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 603 observer.Wait(); 604 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 605} 606 607IN_PROC_BROWSER_TEST_F( 608 PasswordManagerBrowserTest, 609 NoPromptForFailedLoginFromSubFrameWithMultiFramesInPage) { 610 NavigateToFile("/password/multi_frames.html"); 611 612 // Make sure that we don't prompt to save the password for a failed login 613 // from a sub-frame with multiple frames in the same page. 614 NavigationObserver observer(WebContents()); 615 scoped_ptr<PromptObserver> prompt_observer( 616 PromptObserver::Create(WebContents())); 617 std::string fill_and_submit = 618 "var first_frame = document.getElementById('first_frame');" 619 "var frame_doc = first_frame.contentDocument;" 620 "frame_doc.getElementById('username_failed').value = 'temp';" 621 "frame_doc.getElementById('password_failed').value = 'random';" 622 "frame_doc.getElementById('submit_failed').click();" 623 "window.parent.location.href = 'multi_frames.html';"; 624 625 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 626 observer.Wait(); 627 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 628} 629 630IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForXHRSubmit) { 631#if defined(OS_WIN) && defined(USE_ASH) 632 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 633 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 634 return; 635#endif 636 NavigateToFile("/password/password_xhr_submit.html"); 637 638 // Verify that we show the save password prompt if a form returns false 639 // in its onsubmit handler but instead logs in/navigates via XHR. 640 // Note that calling 'submit()' on a form with javascript doesn't call 641 // the onsubmit handler, so we click the submit button instead. 642 NavigationObserver observer(WebContents()); 643 scoped_ptr<PromptObserver> prompt_observer( 644 PromptObserver::Create(WebContents())); 645 std::string fill_and_submit = 646 "document.getElementById('username_field').value = 'temp';" 647 "document.getElementById('password_field').value = 'random';" 648 "document.getElementById('submit_button').click()"; 649 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 650 observer.Wait(); 651 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 652} 653 654IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 655 PromptForXHRWithoutOnSubmit) { 656 NavigateToFile("/password/password_xhr_submit.html"); 657 658 // Verify that if XHR navigation occurs and the form is properly filled out, 659 // we try and save the password even though onsubmit hasn't been called. 660 NavigationObserver observer(WebContents()); 661 scoped_ptr<PromptObserver> prompt_observer( 662 PromptObserver::Create(WebContents())); 663 std::string fill_and_navigate = 664 "document.getElementById('username_field').value = 'temp';" 665 "document.getElementById('password_field').value = 'random';" 666 "send_xhr()"; 667 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate)); 668 observer.Wait(); 669 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 670} 671 672IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptIfLinkClicked) { 673 NavigateToFile("/password/password_form.html"); 674 675 // Verify that if the user takes a direct action to leave the page, we don't 676 // prompt to save the password even if the form is already filled out. 677 NavigationObserver observer(WebContents()); 678 scoped_ptr<PromptObserver> prompt_observer( 679 PromptObserver::Create(WebContents())); 680 std::string fill_and_click_link = 681 "document.getElementById('username_field').value = 'temp';" 682 "document.getElementById('password_field').value = 'random';" 683 "document.getElementById('link').click();"; 684 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_click_link)); 685 observer.Wait(); 686 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 687} 688 689// TODO(jam): http://crbug.com/350550 690#if !defined(OS_WIN) 691IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 692 VerifyPasswordGenerationUpload) { 693 // Prevent Autofill requests from actually going over the wire. 694 net::TestURLFetcherFactory factory; 695 // Disable Autofill requesting access to AddressBook data. This causes 696 // the test to hang on Mac. 697 autofill::test::DisableSystemServices(browser()->profile()->GetPrefs()); 698 699 // Visit a signup form. 700 NavigateToFile("/password/signup_form.html"); 701 702 // Enter a password and save it. 703 NavigationObserver first_observer(WebContents()); 704 scoped_ptr<PromptObserver> prompt_observer( 705 PromptObserver::Create(WebContents())); 706 std::string fill_and_submit = 707 "document.getElementById('other_info').value = 'stuff';" 708 "document.getElementById('username_field').value = 'my_username';" 709 "document.getElementById('password_field').value = 'password';" 710 "document.getElementById('input_submit_button').click()"; 711 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 712 713 first_observer.Wait(); 714 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 715 prompt_observer->Accept(); 716 717 // Now navigate to a login form that has similar HTML markup. 718 NavigateToFile("/password/password_form.html"); 719 720 // Simulate a user click to force an autofill of the form's DOM value, not 721 // just the suggested value. 722 content::SimulateMouseClick( 723 WebContents(), 0, blink::WebMouseEvent::ButtonLeft); 724 725 // The form should be filled with the previously submitted username. 726 std::string get_username = 727 "window.domAutomationController.send(" 728 "document.getElementById('username_field').value);"; 729 std::string actual_username; 730 ASSERT_TRUE(content::ExecuteScriptAndExtractString(RenderViewHost(), 731 get_username, 732 &actual_username)); 733 ASSERT_EQ("my_username", actual_username); 734 735 // Submit the form and verify that there is no infobar (as the password 736 // has already been saved). 737 NavigationObserver second_observer(WebContents()); 738 scoped_ptr<PromptObserver> second_prompt_observer( 739 PromptObserver::Create(WebContents())); 740 std::string submit_form = 741 "document.getElementById('input_submit_button').click()"; 742 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit_form)); 743 second_observer.Wait(); 744 EXPECT_FALSE(second_prompt_observer->IsShowingPrompt()); 745 746 // Verify that we sent two pings to Autofill. One vote for of PASSWORD for 747 // the current form, and one vote for ACCOUNT_CREATION_PASSWORD on the 748 // original form since it has more than 2 text input fields and was used for 749 // the first time on a different form. 750 base::HistogramBase* upload_histogram = 751 base::StatisticsRecorder::FindHistogram( 752 "PasswordGeneration.UploadStarted"); 753 ASSERT_TRUE(upload_histogram); 754 scoped_ptr<base::HistogramSamples> snapshot = 755 upload_histogram->SnapshotSamples(); 756 EXPECT_EQ(0, snapshot->GetCount(0 /* failure */)); 757 EXPECT_EQ(2, snapshot->GetCount(1 /* success */)); 758} 759#endif 760 761IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForSubmitFromIframe) { 762 NavigateToFile("/password/password_submit_from_iframe.html"); 763 764 // Submit a form in an iframe, then cause the whole page to navigate without a 765 // user gesture. We expect the save password prompt to be shown here, because 766 // some pages use such iframes for login forms. 767 NavigationObserver observer(WebContents()); 768 scoped_ptr<PromptObserver> prompt_observer( 769 PromptObserver::Create(WebContents())); 770 std::string fill_and_submit = 771 "var iframe = document.getElementById('test_iframe');" 772 "var iframe_doc = iframe.contentDocument;" 773 "iframe_doc.getElementById('username_field').value = 'temp';" 774 "iframe_doc.getElementById('password_field').value = 'random';" 775 "iframe_doc.getElementById('submit_button').click()"; 776 777 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 778 observer.Wait(); 779 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 780} 781 782IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 783 PromptForInputElementWithoutName) { 784 // Check that the prompt is shown for forms where input elements lack the 785 // "name" attribute but the "id" is present. 786 NavigateToFile("/password/password_form.html"); 787 788 NavigationObserver observer(WebContents()); 789 scoped_ptr<PromptObserver> prompt_observer( 790 PromptObserver::Create(WebContents())); 791 std::string fill_and_submit = 792 "document.getElementById('username_field_no_name').value = 'temp';" 793 "document.getElementById('password_field_no_name').value = 'random';" 794 "document.getElementById('input_submit_button_no_name').click()"; 795 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 796 observer.Wait(); 797 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 798} 799 800IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 801 PromptForInputElementWithoutId) { 802 // Check that the prompt is shown for forms where input elements lack the 803 // "id" attribute but the "name" attribute is present. 804 NavigateToFile("/password/password_form.html"); 805 806 NavigationObserver observer(WebContents()); 807 scoped_ptr<PromptObserver> prompt_observer( 808 PromptObserver::Create(WebContents())); 809 std::string fill_and_submit = 810 "document.getElementsByName('username_field_no_id')[0].value = 'temp';" 811 "document.getElementsByName('password_field_no_id')[0].value = 'random';" 812 "document.getElementsByName('input_submit_button_no_id')[0].click()"; 813 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 814 observer.Wait(); 815 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 816} 817 818IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 819 NoPromptForInputElementWithoutIdAndName) { 820 // Check that no prompt is shown for forms where the input fields lack both 821 // the "id" and the "name" attributes. 822 NavigateToFile("/password/password_form.html"); 823 824 NavigationObserver observer(WebContents()); 825 scoped_ptr<PromptObserver> prompt_observer( 826 PromptObserver::Create(WebContents())); 827 std::string fill_and_submit = 828 "var form = document.getElementById('testform_elements_no_id_no_name');" 829 "var username = form.children[0];" 830 "username.value = 'temp';" 831 "var password = form.children[1];" 832 "password.value = 'random';" 833 "form.children[2].click()"; // form.children[2] is the submit button. 834 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 835 observer.Wait(); 836 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 837} 838 839// Test for checking that no prompt is shown for URLs with file: scheme. 840IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 841 NoPromptForFileSchemeURLs) { 842 GURL url = GetFileURL("password_form.html"); 843 ui_test_utils::NavigateToURL(browser(), url); 844 845 NavigationObserver observer(WebContents()); 846 scoped_ptr<PromptObserver> prompt_observer( 847 PromptObserver::Create(WebContents())); 848 std::string fill_and_submit = 849 "document.getElementById('username_field').value = 'temp';" 850 "document.getElementById('password_field').value = 'random';" 851 "document.getElementById('input_submit_button').click();"; 852 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 853 observer.Wait(); 854 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 855} 856 857IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, DeleteFrameBeforeSubmit) { 858 NavigateToFile("/password/multi_frames.html"); 859 860 NavigationObserver observer(WebContents()); 861 // Make sure we save some password info from an iframe and then destroy it. 862 std::string save_and_remove = 863 "var first_frame = document.getElementById('first_frame');" 864 "var frame_doc = first_frame.contentDocument;" 865 "frame_doc.getElementById('username_field').value = 'temp';" 866 "frame_doc.getElementById('password_field').value = 'random';" 867 "frame_doc.getElementById('input_submit_button').click();" 868 "first_frame.parentNode.removeChild(first_frame);"; 869 // Submit from the main frame, but without navigating through the onsubmit 870 // handler. 871 std::string navigate_frame = 872 "document.getElementById('username_field').value = 'temp';" 873 "document.getElementById('password_field').value = 'random';" 874 "document.getElementById('input_submit_button').click();" 875 "window.location.href = 'done.html';"; 876 877 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), save_and_remove)); 878 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame)); 879 observer.Wait(); 880 // The only thing we check here is that there is no use-after-free reported. 881} 882 883IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PasswordValueAccessible) { 884 NavigateToFile("/password/form_and_link.html"); 885 886 // Click on a link to open a new tab, then switch back to the first one. 887 EXPECT_EQ(1, browser()->tab_strip_model()->count()); 888 std::string click = 889 "document.getElementById('testlink').click();"; 890 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), click)); 891 EXPECT_EQ(2, browser()->tab_strip_model()->count()); 892 browser()->tab_strip_model()->ActivateTabAt(0, false); 893 894 // Fill in the credentials, and make sure they are saved. 895 NavigationObserver form_submit_observer(WebContents()); 896 scoped_ptr<PromptObserver> prompt_observer( 897 PromptObserver::Create(WebContents())); 898 std::string fill_and_submit = 899 "document.getElementById('username_field').value = 'temp';" 900 "document.getElementById('password_field').value = 'random';" 901 "document.getElementById('input_submit_button').click();"; 902 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 903 form_submit_observer.Wait(); 904 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 905 prompt_observer->Accept(); 906 907 // Reload the original page to have the saved credentials autofilled. 908 NavigationObserver reload_observer(WebContents()); 909 NavigateToFile("/password/form_and_link.html"); 910 reload_observer.Wait(); 911 912 // Wait until the username is filled, to make sure autofill kicked in. 913 WaitForElementValue("username_field", "temp"); 914 // Now check that the password is not accessible yet. 915 CheckElementValue("password_field", ""); 916 // Let the user interact with the page. 917 content::SimulateMouseClickAt( 918 WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(1, 1)); 919 // Wait until that interaction causes the password value to be revealed. 920 WaitForElementValue("password_field", "random"); 921 // And check that after the side-effects of the interaction took place, the 922 // username value stays the same. 923 CheckElementValue("username_field", "temp"); 924} 925 926// The following test is limited to Aura, because 927// RenderWidgetHostViewGuest::ProcessAckedTouchEvent is, and 928// ProcessAckedTouchEvent is what triggers the translation of touch events to 929// gesture events. 930#if defined(USE_AURA) 931IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 932 PasswordValueAccessibleOnSubmit) { 933 NavigateToFile("/password/form_and_link.html"); 934 935 // Fill in the credentials, and make sure they are saved. 936 NavigationObserver form_submit_observer(WebContents()); 937 scoped_ptr<PromptObserver> prompt_observer( 938 PromptObserver::Create(WebContents())); 939 std::string fill_and_submit = 940 "document.getElementById('username_field').value = 'temp';" 941 "document.getElementById('password_field').value = 'random_secret';" 942 "document.getElementById('input_submit_button').click();"; 943 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 944 form_submit_observer.Wait(); 945 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 946 prompt_observer->Accept(); 947 948 // Reload the original page to have the saved credentials autofilled. 949 NavigationObserver reload_observer(WebContents()); 950 NavigateToFile("/password/form_and_link.html"); 951 reload_observer.Wait(); 952 953 NavigationObserver submit_observer(WebContents()); 954 // Submit the form via a tap on the submit button. The button is placed at 0, 955 // 100, and has height 300 and width 700. 956 content::SimulateTapAt(WebContents(), gfx::Point(350, 250)); 957 submit_observer.Wait(); 958 std::string query = WebContents()->GetURL().query(); 959 EXPECT_NE(std::string::npos, query.find("random_secret")) << query; 960} 961#endif 962 963// Test fix for crbug.com/338650. 964IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 965 DontPromptForPasswordFormWithDefaultValue) { 966 NavigateToFile("/password/password_form_with_default_value.html"); 967 968 // Don't prompt if we navigate away even if there is a password value since 969 // it's not coming from the user. 970 NavigationObserver observer(WebContents()); 971 scoped_ptr<PromptObserver> prompt_observer( 972 PromptObserver::Create(WebContents())); 973 NavigateToFile("/password/done.html"); 974 observer.Wait(); 975 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 976} 977 978IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 979 PromptWhenEnableAutomaticPasswordSavingSwitchIsNotSet) { 980 NavigateToFile("/password/password_form.html"); 981 982 // Fill a form and submit through a <input type="submit"> button. 983 NavigationObserver observer(WebContents()); 984 scoped_ptr<PromptObserver> prompt_observer( 985 PromptObserver::Create(WebContents())); 986 std::string fill_and_submit = 987 "document.getElementById('username_field').value = 'temp';" 988 "document.getElementById('password_field').value = 'random';" 989 "document.getElementById('input_submit_button').click()"; 990 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 991 observer.Wait(); 992 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 993} 994 995IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 996 DontPromptWhenEnableAutomaticPasswordSavingSwitchIsSet) { 997 password_manager::TestPasswordStore* password_store = 998 static_cast<password_manager::TestPasswordStore*>( 999 PasswordStoreFactory::GetForProfile(browser()->profile(), 1000 Profile::IMPLICIT_ACCESS).get()); 1001 1002 EXPECT_TRUE(password_store->IsEmpty()); 1003 1004 NavigateToFile("/password/password_form.html"); 1005 1006 // Add the enable-automatic-password-saving switch. 1007 CommandLine::ForCurrentProcess()->AppendSwitch( 1008 password_manager::switches::kEnableAutomaticPasswordSaving); 1009 1010 // Fill a form and submit through a <input type="submit"> button. 1011 NavigationObserver observer(WebContents()); 1012 scoped_ptr<PromptObserver> prompt_observer( 1013 PromptObserver::Create(WebContents())); 1014 // Make sure that the only passwords saved are the auto-saved ones. 1015 std::string fill_and_submit = 1016 "document.getElementById('username_field').value = 'temp';" 1017 "document.getElementById('password_field').value = 'random';" 1018 "document.getElementById('input_submit_button').click()"; 1019 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); 1020 observer.Wait(); 1021 if (chrome::VersionInfo::GetChannel() == 1022 chrome::VersionInfo::CHANNEL_UNKNOWN) { 1023 // Passwords getting auto-saved, no prompt. 1024 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 1025 EXPECT_FALSE(password_store->IsEmpty()); 1026 } else { 1027 // Prompt shown, and no passwords saved automatically. 1028 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 1029 EXPECT_TRUE(password_store->IsEmpty()); 1030 } 1031} 1032 1033// Test fix for crbug.com/368690. 1034IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptWhenReloading) { 1035 NavigateToFile("/password/password_form.html"); 1036 1037 std::string fill = 1038 "document.getElementById('username_redirect').value = 'temp';" 1039 "document.getElementById('password_redirect').value = 'random';"; 1040 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill)); 1041 1042 NavigationObserver observer(WebContents()); 1043 scoped_ptr<PromptObserver> prompt_observer( 1044 PromptObserver::Create(WebContents())); 1045 GURL url = embedded_test_server()->GetURL("/password/password_form.html"); 1046 chrome::NavigateParams params(browser(), url, 1047 ui::PAGE_TRANSITION_RELOAD); 1048 ui_test_utils::NavigateToURL(¶ms); 1049 observer.Wait(); 1050 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 1051} 1052 1053// Test that if a form gets dynamically added between the form parsing and 1054// rendering, and while the main frame still loads, it still is registered, and 1055// thus saving passwords from it works. 1056IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 1057 FormsAddedBetweenParsingAndRendering) { 1058 NavigateToFile("/password/between_parsing_and_rendering.html"); 1059 1060 NavigationObserver observer(WebContents()); 1061 scoped_ptr<PromptObserver> prompt_observer( 1062 PromptObserver::Create(WebContents())); 1063 std::string submit = 1064 "document.getElementById('username').value = 'temp';" 1065 "document.getElementById('password').value = 'random';" 1066 "document.getElementById('submit-button').click();"; 1067 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit)); 1068 observer.Wait(); 1069 1070 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 1071} 1072 1073// Test that if there was no previous page load then the PasswordManagerDriver 1074// does not think that there were SSL errors on the current page. The test opens 1075// a new tab with a URL for which the embedded test server issues a basic auth 1076// challenge. 1077IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoLastLoadGoodLastLoad) { 1078 // Teach the embedded server to handle requests by issuing the basic auth 1079 // challenge. 1080 embedded_test_server()->RegisterRequestHandler( 1081 base::Bind(&HandleTestAuthRequest)); 1082 1083 LoginPromptBrowserTestObserver login_observer; 1084 // We need to register to all sources, because the navigation observer we are 1085 // interested in is for a new tab to be opened, and thus does not exist yet. 1086 login_observer.Register(content::NotificationService::AllSources()); 1087 1088 password_manager::TestPasswordStore* password_store = 1089 static_cast<password_manager::TestPasswordStore*>( 1090 PasswordStoreFactory::GetForProfile(browser()->profile(), 1091 Profile::IMPLICIT_ACCESS).get()); 1092 EXPECT_TRUE(password_store->IsEmpty()); 1093 1094 // Navigate to a page requiring HTTP auth. Wait for the tab to get the correct 1095 // WebContents, but don't wait for navigation, which only finishes after 1096 // authentication. 1097 ui_test_utils::NavigateToURLWithDisposition( 1098 browser(), 1099 embedded_test_server()->GetURL("/basic_auth"), 1100 NEW_FOREGROUND_TAB, 1101 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); 1102 1103 content::NavigationController* nav_controller = 1104 &WebContents()->GetController(); 1105 NavigationObserver nav_observer(WebContents()); 1106 scoped_ptr<PromptObserver> prompt_observer( 1107 PromptObserver::Create(WebContents())); 1108 WindowedAuthNeededObserver auth_needed_observer(nav_controller); 1109 auth_needed_observer.Wait(); 1110 1111 WindowedAuthSuppliedObserver auth_supplied_observer(nav_controller); 1112 // Offer valid credentials on the auth challenge. 1113 ASSERT_EQ(1u, login_observer.handlers().size()); 1114 LoginHandler* handler = *login_observer.handlers().begin(); 1115 ASSERT_TRUE(handler); 1116 // Any username/password will work. 1117 handler->SetAuth(base::UTF8ToUTF16("user"), base::UTF8ToUTF16("pwd")); 1118 auth_supplied_observer.Wait(); 1119 1120 // The password manager should be working correctly. 1121 nav_observer.Wait(); 1122 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 1123 prompt_observer->Accept(); 1124 1125 // Spin the message loop to make sure the password store had a chance to save 1126 // the password. 1127 base::RunLoop run_loop; 1128 run_loop.RunUntilIdle(); 1129 EXPECT_FALSE(password_store->IsEmpty()); 1130} 1131 1132// In some situations, multiple PasswordFormManager instances from 1133// PasswordManager::pending_login_managers_ would match (via DoesManage) a form 1134// to be provisionally saved. One of them might be a complete match, the other 1135// all-but-action match. Normally, the former should be preferred, but if the 1136// former has not finished matching, and the latter has, the latter should be 1137// used (otherwise we'd give up even though we could have saved the password). 1138IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 1139 PreferPasswordFormManagerWhichFinishedMatching) { 1140 NavigateToFile("/password/create_form_copy_on_submit.html"); 1141 1142 NavigationObserver observer(WebContents()); 1143 scoped_ptr<PromptObserver> prompt_observer( 1144 PromptObserver::Create(WebContents())); 1145 std::string submit = 1146 "document.getElementById('username').value = 'overwrite_me';" 1147 "document.getElementById('password').value = 'random';" 1148 "document.getElementById('non-form-button').click();"; 1149 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit)); 1150 observer.Wait(); 1151 1152 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 1153} 1154 1155// Test that if login fails and content server pushes a different login form 1156// with action URL having different schemes. Heuristic shall be able 1157// identify such cases and *shall not* prompt to save incorrect password. 1158IN_PROC_BROWSER_TEST_F( 1159 PasswordManagerBrowserTest, 1160 NoPromptForLoginFailedAndServerPushSeperateLoginForm_HttpToHttps) { 1161 std::string path = 1162 "/password/separate_login_form_with_onload_submit_script.html"; 1163 GURL http_url(embedded_test_server()->GetURL(path)); 1164 ASSERT_TRUE(http_url.SchemeIs(url::kHttpScheme)); 1165 1166 NavigationObserver observer(WebContents()); 1167 scoped_ptr<PromptObserver> prompt_observer( 1168 PromptObserver::Create(WebContents())); 1169 ui_test_utils::NavigateToURL(browser(), http_url); 1170 1171 observer.SetPathToWaitFor("/password/done_and_separate_login_form.html"); 1172 observer.Wait(); 1173 1174 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 1175} 1176 1177IN_PROC_BROWSER_TEST_F( 1178 PasswordManagerBrowserTest, 1179 NoPromptForLoginFailedAndServerPushSeperateLoginForm_HttpsToHttp) { 1180 CommandLine::ForCurrentProcess()->AppendSwitch( 1181 switches::kAllowRunningInsecureContent); 1182 CommandLine::ForCurrentProcess()->AppendSwitch( 1183 switches::kIgnoreCertificateErrors); 1184 const base::FilePath::CharType kDocRoot[] = 1185 FILE_PATH_LITERAL("chrome/test/data"); 1186 net::SpawnedTestServer https_test_server( 1187 net::SpawnedTestServer::TYPE_HTTPS, 1188 net::SpawnedTestServer::SSLOptions( 1189 net::SpawnedTestServer::SSLOptions::CERT_OK), 1190 base::FilePath(kDocRoot)); 1191 ASSERT_TRUE(https_test_server.Start()); 1192 1193 // This test case cannot inject the scripts via content::ExecuteScript() in 1194 // files served through HTTPS. Therefore the scripts are made part of the HTML 1195 // site and executed on load. 1196 std::string path = 1197 "password/separate_login_form_with_onload_submit_script.html"; 1198 GURL https_url(https_test_server.GetURL(path)); 1199 ASSERT_TRUE(https_url.SchemeIs(url::kHttpsScheme)); 1200 1201 NavigationObserver observer(WebContents()); 1202 scoped_ptr<PromptObserver> prompt_observer( 1203 PromptObserver::Create(WebContents())); 1204 ui_test_utils::NavigateToURL(browser(), https_url); 1205 1206 observer.SetPathToWaitFor("/password/done_and_separate_login_form.html"); 1207 observer.Wait(); 1208 1209 EXPECT_FALSE(prompt_observer->IsShowingPrompt()); 1210} 1211 1212IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 1213 PromptWhenPasswordFormWithoutUsernameFieldSubmitted) { 1214 password_manager::TestPasswordStore* password_store = 1215 static_cast<password_manager::TestPasswordStore*>( 1216 PasswordStoreFactory::GetForProfile(browser()->profile(), 1217 Profile::IMPLICIT_ACCESS).get()); 1218 1219 EXPECT_TRUE(password_store->IsEmpty()); 1220 1221 NavigateToFile("/password/form_with_only_password_field.html"); 1222 1223 NavigationObserver observer(WebContents()); 1224 scoped_ptr<PromptObserver> prompt_observer( 1225 PromptObserver::Create(WebContents())); 1226 std::string submit = 1227 "document.getElementById('password').value = 'password';" 1228 "document.getElementById('submit-button').click();"; 1229 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit)); 1230 observer.Wait(); 1231 1232 EXPECT_TRUE(prompt_observer->IsShowingPrompt()); 1233 prompt_observer->Accept(); 1234 1235 // Spin the message loop to make sure the password store had a chance to save 1236 // the password. 1237 base::RunLoop run_loop; 1238 run_loop.RunUntilIdle(); 1239 EXPECT_FALSE(password_store->IsEmpty()); 1240} 1241 1242IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, 1243 AutofillSuggetionsForPasswordFormWithoutUsernameField) { 1244 password_manager::TestPasswordStore* password_store = 1245 static_cast<password_manager::TestPasswordStore*>( 1246 PasswordStoreFactory::GetForProfile(browser()->profile(), 1247 Profile::IMPLICIT_ACCESS).get()); 1248 1249 EXPECT_TRUE(password_store->IsEmpty()); 1250 1251 // Password form without username-field. 1252 NavigateToFile("/password/form_with_only_password_field.html"); 1253 1254 NavigationObserver observer(WebContents()); 1255 scoped_ptr<PromptObserver> prompt_observer( 1256 PromptObserver::Create(WebContents())); 1257 std::string submit = 1258 "document.getElementById('password').value = 'mypassword';" 1259 "document.getElementById('submit-button').click();"; 1260 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit)); 1261 observer.Wait(); 1262 1263 prompt_observer->Accept(); 1264 1265 // Spin the message loop to make sure the password store had a chance to save 1266 // the password. 1267 base::RunLoop run_loop; 1268 run_loop.RunUntilIdle(); 1269 EXPECT_FALSE(password_store->IsEmpty()); 1270 1271 // Now, navigate to same html password form and verify whether password is 1272 // autofilled. 1273 NavigateToFile("/password/form_with_only_password_field.html"); 1274 1275 // Let the user interact with the page, so that DOM gets modification events, 1276 // needed for autofilling fields. 1277 content::SimulateMouseClickAt( 1278 WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(1, 1)); 1279 1280 // Wait until that interaction causes the password value to be revealed. 1281 WaitForElementValue("password", "mypassword"); 1282} 1283