// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "base/command_line.h" #include "base/metrics/histogram_samples.h" #include "base/metrics/statistics_recorder.h" #include "base/path_service.h" #include "base/run_loop.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/password_manager/chrome_password_manager_client.h" #include "chrome/browser/password_manager/password_manager_test_base.h" #include "chrome/browser/password_manager/password_store_factory.h" #include "chrome/browser/password_manager/test_password_store_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/login/login_prompt.h" #include "chrome/browser/ui/login/login_prompt_test_utils.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_version_info.h" #include "chrome/test/base/test_switches.h" #include "chrome/test/base/ui_test_utils.h" #include "components/autofill/content/common/autofill_messages.h" #include "components/autofill/core/browser/autofill_test_utils.h" #include "components/autofill/core/browser/test_autofill_client.h" #include "components/autofill/core/common/password_form.h" #include "components/password_manager/content/browser/content_password_manager_driver.h" #include "components/password_manager/content/browser/content_password_manager_driver_factory.h" #include "components/password_manager/core/browser/test_password_store.h" #include "components/password_manager/core/common/password_manager_switches.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" #include "ipc/ipc_security_test_util.h" #include "net/base/filename_util.h" #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_response.h" #include "net/test/spawned_test_server/spawned_test_server.h" #include "net/url_request/test_url_fetcher_factory.h" #include "testing/gmock/include/gmock/gmock.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/geometry/point.h" namespace { GURL GetFileURL(const char* filename) { base::FilePath path; PathService::Get(chrome::DIR_TEST_DATA, &path); path = path.AppendASCII("password").AppendASCII(filename); CHECK(base::PathExists(path)); return net::FilePathToFileURL(path); } // Handles |request| to "/basic_auth". If "Authorization" header is present, // responds with a non-empty HTTP 200 page (regardless of its value). Otherwise // serves a Basic Auth challenge. scoped_ptr HandleTestAuthRequest( const net::test_server::HttpRequest& request) { if (!base::StartsWith(request.relative_url, "/basic_auth", base::CompareCase::SENSITIVE)) return scoped_ptr(); if (ContainsKey(request.headers, "Authorization")) { scoped_ptr http_response( new net::test_server::BasicHttpResponse); http_response->set_code(net::HTTP_OK); http_response->set_content("Success!"); return http_response.Pass(); } else { scoped_ptr http_response( new net::test_server::BasicHttpResponse); http_response->set_code(net::HTTP_UNAUTHORIZED); http_response->AddCustomHeader("WWW-Authenticate", "Basic realm=\"test realm\""); return http_response.Pass(); } } class ObservingAutofillClient : public autofill::TestAutofillClient { public: ObservingAutofillClient() : message_loop_runner_(new content::MessageLoopRunner) {} ~ObservingAutofillClient() override {} void Wait() { message_loop_runner_->Run(); } void ShowAutofillPopup( const gfx::RectF& element_bounds, base::i18n::TextDirection text_direction, const std::vector& suggestions, base::WeakPtr delegate) override { message_loop_runner_->Quit(); } private: scoped_refptr message_loop_runner_; DISALLOW_COPY_AND_ASSIGN(ObservingAutofillClient); }; } // namespace namespace password_manager { // Actual tests --------------------------------------------------------------- IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, PromptForNormalSubmit) { NavigateToFile("/password/password_form.html"); // Fill a form and submit through a button. Nothing // special. NavigationObserver observer(WebContents()); scoped_ptr prompt_observer( PromptObserver::Create(WebContents())); std::string fill_and_submit = "document.getElementById('username_field').value = 'temp';" "document.getElementById('password_field').value = 'random';" "document.getElementById('input_submit_button').click()"; ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); observer.Wait(); EXPECT_TRUE(prompt_observer->IsShowingPrompt()); } IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, PromptForSubmitWithInPageNavigation) { NavigateToFile("/password/password_navigate_before_submit.html"); // Fill a form and submit through a button. Nothing // special. The form does an in-page navigation before submitting. NavigationObserver observer(WebContents()); scoped_ptr prompt_observer( PromptObserver::Create(WebContents())); std::string fill_and_submit = "document.getElementById('username_field').value = 'temp';" "document.getElementById('password_field').value = 'random';" "document.getElementById('input_submit_button').click()"; ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); observer.Wait(); EXPECT_TRUE(prompt_observer->IsShowingPrompt()); } IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, LoginSuccessWithUnrelatedForm) { // Log in, see a form on the landing page. That form is not related to the // login form (=has a different action), so we should offer saving the // password. NavigateToFile("/password/password_form.html"); NavigationObserver observer(WebContents()); scoped_ptr prompt_observer( PromptObserver::Create(WebContents())); std::string fill_and_submit = "document.getElementById('username_unrelated').value = 'temp';" "document.getElementById('password_unrelated').value = 'random';" "document.getElementById('submit_unrelated').click()"; ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); observer.Wait(); EXPECT_TRUE(prompt_observer->IsShowingPrompt()); } IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, LoginFailed) { // Log in, see a form on the landing page. That form is not related to the // login form (=has a different action), so we should offer saving the // password. NavigateToFile("/password/password_form.html"); NavigationObserver observer(WebContents()); scoped_ptr prompt_observer( PromptObserver::Create(WebContents())); std::string fill_and_submit = "document.getElementById('username_failed').value = 'temp';" "document.getElementById('password_failed').value = 'random';" "document.getElementById('submit_failed').click()"; ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); observer.Wait(); EXPECT_FALSE(prompt_observer->IsShowingPrompt()); } IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, Redirects) { NavigateToFile("/password/password_form.html"); // Fill a form and submit through a button. The form // points to a redirection page. NavigationObserver observer(WebContents()); scoped_ptr prompt_observer( PromptObserver::Create(WebContents())); std::string fill_and_submit = "document.getElementById('username_redirect').value = 'temp';" "document.getElementById('password_redirect').value = 'random';" "document.getElementById('submit_redirect').click()"; ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); observer.Wait(); EXPECT_TRUE(prompt_observer->IsShowingPrompt()); // The redirection page now redirects via Javascript. We check that the // infobar stays. ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), "window.location.href = 'done.html';")); observer.Wait(); EXPECT_TRUE(prompt_observer->IsShowingPrompt()); } IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTestBase, PromptForSubmitUsingJavaScript) { NavigateToFile("/password/password_form.html"); // Fill a form and submit using