| // 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 <string> |
| |
| #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/infobars/infobar_service.h" |
| #include "chrome/browser/password_manager/chrome_password_manager_client.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/passwords/manage_passwords_ui_controller.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/in_process_browser_test.h" |
| #include "chrome/test/base/test_switches.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/autofill/core/browser/autofill_test_utils.h" |
| #include "components/autofill/core/browser/test_autofill_client.h" |
| #include "components/infobars/core/confirm_infobar_delegate.h" |
| #include "components/infobars/core/infobar.h" |
| #include "components/infobars/core/infobar_manager.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_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 "net/base/filename_util.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" |
| |
| |
| // NavigationObserver --------------------------------------------------------- |
| |
| namespace { |
| |
| // Observer that waits for navigation to complete and for the password infobar |
| // to be shown. |
| class NavigationObserver : public content::WebContentsObserver { |
| public: |
| explicit NavigationObserver(content::WebContents* web_contents) |
| : content::WebContentsObserver(web_contents), |
| quit_on_entry_commited_(false), |
| message_loop_runner_(new content::MessageLoopRunner) {} |
| |
| ~NavigationObserver() override {} |
| |
| // Normally Wait() will not return until a main frame navigation occurs. |
| // If a path is set, Wait() will return after this path has been seen, |
| // regardless of the frame that navigated. Useful for multi-frame pages. |
| void SetPathToWaitFor(const std::string& path) { |
| wait_for_path_ = path; |
| } |
| |
| // Normally Wait() will not return until a main frame navigation occurs. |
| // If quit_on_entry_commited is true Wait() will return on EntryCommited. |
| void SetQuitOnEntryCommitted(bool quit_on_entry_commited) { |
| quit_on_entry_commited_ = quit_on_entry_commited; |
| } |
| |
| // content::WebContentsObserver: |
| void DidFinishLoad(content::RenderFrameHost* render_frame_host, |
| const GURL& validated_url) override { |
| if (!wait_for_path_.empty()) { |
| if (validated_url.path() == wait_for_path_) |
| message_loop_runner_->Quit(); |
| } else if (!render_frame_host->GetParent()) { |
| message_loop_runner_->Quit(); |
| } |
| } |
| void NavigationEntryCommitted( |
| const content::LoadCommittedDetails& load_details) override { |
| if (quit_on_entry_commited_) |
| message_loop_runner_->Quit(); |
| } |
| void Wait() { message_loop_runner_->Run(); } |
| |
| private: |
| std::string wait_for_path_; |
| bool quit_on_entry_commited_; |
| scoped_refptr<content::MessageLoopRunner> message_loop_runner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(NavigationObserver); |
| }; |
| |
| // Observes the save password prompt (bubble or infobar) for a specified |
| // WebContents, keeps track of whether or not it is currently shown, and allows |
| // accepting saving passwords through it. |
| class PromptObserver { |
| public: |
| virtual ~PromptObserver() {} |
| |
| // Checks if the prompt is being currently shown. |
| virtual bool IsShowingPrompt() const = 0; |
| |
| // Expecting that the prompt is shown, saves the password. Checks that the |
| // prompt is no longer visible afterwards. |
| void Accept() const { |
| EXPECT_TRUE(IsShowingPrompt()); |
| AcceptImpl(); |
| } |
| |
| // Chooses the right implementation of PromptObserver and creates an instance |
| // of it. |
| static scoped_ptr<PromptObserver> Create(content::WebContents* web_contents); |
| |
| protected: |
| PromptObserver() {} |
| |
| // Accepts the password. The implementation can assume that the prompt is |
| // currently shown, but is required to verify that the prompt is eventually |
| // closed. |
| virtual void AcceptImpl() const = 0; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(PromptObserver); |
| }; |
| |
| class InfoBarObserver : public PromptObserver, |
| public infobars::InfoBarManager::Observer { |
| public: |
| explicit InfoBarObserver(content::WebContents* web_contents) |
| : infobar_is_being_shown_(false), |
| infobar_service_(InfoBarService::FromWebContents(web_contents)) { |
| infobar_service_->AddObserver(this); |
| } |
| |
| ~InfoBarObserver() override { |
| if (infobar_service_) |
| infobar_service_->RemoveObserver(this); |
| } |
| |
| private: |
| // PromptObserver: |
| bool IsShowingPrompt() const override { return infobar_is_being_shown_; } |
| |
| void AcceptImpl() const override { |
| EXPECT_EQ(1u, infobar_service_->infobar_count()); |
| if (!infobar_service_->infobar_count()) |
| return; // Let the test finish to gather possibly more diagnostics. |
| |
| // ConfirmInfoBarDelegate::Accept returning true means the infobar is |
| // immediately closed. Checking the return value is preferred to testing |
| // IsShowingPrompt() here, for it avoids the delay until the closing |
| // notification is received. |
| EXPECT_TRUE(infobar_service_->infobar_at(0) |
| ->delegate() |
| ->AsConfirmInfoBarDelegate() |
| ->Accept()); |
| } |
| |
| // infobars::InfoBarManager::Observer: |
| void OnInfoBarAdded(infobars::InfoBar* infobar) override { |
| infobar_is_being_shown_ = true; |
| } |
| |
| void OnInfoBarRemoved(infobars::InfoBar* infobar, bool animate) override { |
| infobar_is_being_shown_ = false; |
| } |
| |
| void OnManagerShuttingDown(infobars::InfoBarManager* manager) override { |
| ASSERT_EQ(infobar_service_, manager); |
| infobar_service_->RemoveObserver(this); |
| infobar_service_ = NULL; |
| } |
| |
| bool infobar_is_being_shown_; |
| InfoBarService* infobar_service_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InfoBarObserver); |
| }; |
| |
| class BubbleObserver : public PromptObserver { |
| public: |
| explicit BubbleObserver(content::WebContents* web_contents) |
| : ui_controller_( |
| ManagePasswordsUIController::FromWebContents(web_contents)) {} |
| |
| ~BubbleObserver() override {} |
| |
| private: |
| // PromptObserver: |
| bool IsShowingPrompt() const override { |
| return ui_controller_->PasswordPendingUserDecision(); |
| } |
| |
| void AcceptImpl() const override { |
| ui_controller_->SavePassword(); |
| EXPECT_FALSE(IsShowingPrompt()); |
| } |
| |
| ManagePasswordsUIController* const ui_controller_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BubbleObserver); |
| }; |
| |
| 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<autofill::Suggestion>& suggestions, |
| base::WeakPtr<autofill::AutofillPopupDelegate> delegate) override { |
| message_loop_runner_->Quit(); |
| } |
| |
| private: |
| scoped_refptr<content::MessageLoopRunner> message_loop_runner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ObservingAutofillClient); |
| }; |
| |
| 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); |
| } |
| |
| // static |
| scoped_ptr<PromptObserver> PromptObserver::Create( |
| content::WebContents* web_contents) { |
| if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) { |
| return scoped_ptr<PromptObserver>(new BubbleObserver(web_contents)); |
| } else { |
| return scoped_ptr<PromptObserver>(new InfoBarObserver(web_contents)); |
| } |
| } |
| |
| // 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<net::test_server::HttpResponse> HandleTestAuthRequest( |
| const net::test_server::HttpRequest& request) { |
| if (!StartsWithASCII(request.relative_url, "/basic_auth", true)) |
| return scoped_ptr<net::test_server::HttpResponse>(); |
| |
| if (ContainsKey(request.headers, "Authorization")) { |
| scoped_ptr<net::test_server::BasicHttpResponse> 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<net::test_server::BasicHttpResponse> 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(); |
| } |
| } |
| |
| } // namespace |
| |
| |
| // PasswordManagerBrowserTest ------------------------------------------------- |
| |
| class PasswordManagerBrowserTest : public InProcessBrowserTest { |
| public: |
| PasswordManagerBrowserTest() {} |
| ~PasswordManagerBrowserTest() override {} |
| |
| // InProcessBrowserTest: |
| void SetUpOnMainThread() override { |
| // Use TestPasswordStore to remove a possible race. Normally the |
| // PasswordStore does its database manipulation on the DB thread, which |
| // creates a possible race during navigation. Specifically the |
| // PasswordManager will ignore any forms in a page if the load from the |
| // PasswordStore has not completed. |
| PasswordStoreFactory::GetInstance()->SetTestingFactory( |
| browser()->profile(), TestPasswordStoreService::Build); |
| ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
| ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch( |
| password_manager::switches::kEnableAutomaticPasswordSaving)); |
| } |
| |
| void TearDownOnMainThread() override { |
| ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); |
| } |
| |
| protected: |
| content::WebContents* WebContents() { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| content::RenderViewHost* RenderViewHost() { |
| return WebContents()->GetRenderViewHost(); |
| } |
| |
| // Wrapper around ui_test_utils::NavigateToURL that waits until |
| // DidFinishLoad() fires. Normally this function returns after |
| // DidStopLoading(), which caused flakiness as the NavigationObserver |
| // would sometimes see the DidFinishLoad event from a previous navigation and |
| // return immediately. |
| void NavigateToFile(const std::string& path) { |
| NavigationObserver observer(WebContents()); |
| GURL url = embedded_test_server()->GetURL(path); |
| ui_test_utils::NavigateToURL(browser(), url); |
| observer.Wait(); |
| } |
| |
| // Waits until the "value" attribute of the HTML element with |element_id| is |
| // equal to |expected_value|. If the current value is not as expected, this |
| // waits until the "change" event is fired for the element. This also |
| // guarantees that once the real value matches the expected, the JavaScript |
| // event loop is spun to allow all other possible events to take place. |
| void WaitForElementValue(const std::string& element_id, |
| const std::string& expected_value); |
| // Checks that the current "value" attribute of the HTML element with |
| // |element_id| is equal to |expected_value|. |
| void CheckElementValue(const std::string& element_id, |
| const std::string& expected_value); |
| |
| // TODO(dvadym): Remove this once history.pushState() handling is not behind |
| // a flag. |
| // Calling this will activate handling of pushState()-initiated form submits. |
| // This feature is currently behind a renderer flag. Just setting the flag in |
| // the browser will not change it for the already running renderer at tab 0. |
| // Therefore this method first sets the flag and then opens a new tab at |
| // position 0. This new tab gets the flag copied from the browser process. |
| void ActivateHistoryPushState(); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest); |
| }; |
| |
| void PasswordManagerBrowserTest::WaitForElementValue( |
| const std::string& element_id, |
| const std::string& expected_value) { |
| enum ReturnCodes { // Possible results of the JavaScript code. |
| RETURN_CODE_OK, |
| RETURN_CODE_NO_ELEMENT, |
| RETURN_CODE_WRONG_VALUE, |
| RETURN_CODE_INVALID, |
| }; |
| const std::string value_check_function = base::StringPrintf( |
| "function valueCheck() {" |
| " var element = document.getElementById('%s');" |
| " return element && element.value == '%s';" |
| "}", |
| element_id.c_str(), |
| expected_value.c_str()); |
| const std::string script = |
| value_check_function + |
| base::StringPrintf( |
| "if (valueCheck()) {" |
| " /* Spin the event loop with setTimeout. */" |
| " setTimeout(window.domAutomationController.send(%d), 0);" |
| "} else {" |
| " var element = document.getElementById('%s');" |
| " if (!element)" |
| " window.domAutomationController.send(%d);" |
| " element.onchange = function() {" |
| " if (valueCheck()) {" |
| " /* Spin the event loop with setTimeout. */" |
| " setTimeout(window.domAutomationController.send(%d), 0);" |
| " } else {" |
| " window.domAutomationController.send(%d);" |
| " }" |
| " };" |
| "}", |
| RETURN_CODE_OK, |
| element_id.c_str(), |
| RETURN_CODE_NO_ELEMENT, |
| RETURN_CODE_OK, |
| RETURN_CODE_WRONG_VALUE); |
| int return_value = RETURN_CODE_INVALID; |
| ASSERT_TRUE(content::ExecuteScriptAndExtractInt( |
| RenderViewHost(), script, &return_value)); |
| EXPECT_EQ(RETURN_CODE_OK, return_value) |
| << "element_id = " << element_id |
| << ", expected_value = " << expected_value; |
| } |
| |
| void PasswordManagerBrowserTest::CheckElementValue( |
| const std::string& element_id, |
| const std::string& expected_value) { |
| const std::string value_check_script = base::StringPrintf( |
| "var element = document.getElementById('%s');" |
| "window.domAutomationController.send(element && element.value == '%s');", |
| element_id.c_str(), |
| expected_value.c_str()); |
| bool return_value = false; |
| ASSERT_TRUE(content::ExecuteScriptAndExtractBool( |
| RenderViewHost(), value_check_script, &return_value)); |
| EXPECT_TRUE(return_value) << "element_id = " << element_id |
| << ", expected_value = " << expected_value; |
| } |
| |
| void PasswordManagerBrowserTest::ActivateHistoryPushState() { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| autofill::switches::kEnablePasswordSaveOnInPageNavigation); |
| AddTabAtIndex(0, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED); |
| } |
| |
| // Actual tests --------------------------------------------------------------- |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| PromptForNormalSubmit) { |
| NavigateToFile("/password/password_form.html"); |
| |
| // Fill a form and submit through a <input type="submit"> button. Nothing |
| // special. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> 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(PasswordManagerBrowserTest, |
| PromptForSubmitWithInPageNavigation) { |
| NavigateToFile("/password/password_navigate_before_submit.html"); |
| |
| // Fill a form and submit through a <input type="submit"> button. Nothing |
| // special. The form does an in-page navigation before submitting. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> 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(PasswordManagerBrowserTest, |
| 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<PromptObserver> 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(PasswordManagerBrowserTest, 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<PromptObserver> 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(PasswordManagerBrowserTest, Redirects) { |
| NavigateToFile("/password/password_form.html"); |
| |
| // Fill a form and submit through a <input type="submit"> button. The form |
| // points to a redirection page. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> 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(PasswordManagerBrowserTest, |
| PromptForSubmitUsingJavaScript) { |
| NavigateToFile("/password/password_form.html"); |
| |
| // Fill a form and submit using <button> that calls submit() on the form. |
| // This should work regardless of the type of element, as long as submit() is |
| // called. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementById('username_field').value = 'temp';" |
| "document.getElementById('password_field').value = 'random';" |
| "document.getElementById('submit_button').click()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| // Flaky: crbug.com/301547, observed on win and mac. Probably happens on all |
| // platforms. |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| DISABLED_PromptForDynamicForm) { |
| NavigateToFile("/password/dynamic_password_form.html"); |
| |
| // Fill the dynamic password form and submit. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementById('create_form_button').click();" |
| "window.setTimeout(function() {" |
| " document.dynamic_form.username.value = 'tempro';" |
| " document.dynamic_form.password.value = 'random';" |
| " document.dynamic_form.submit();" |
| "}, 0)"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForNavigation) { |
| NavigateToFile("/password/password_form.html"); |
| |
| // Don't fill the password form, just navigate away. Shouldn't prompt. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), |
| "window.location.href = 'done.html';")); |
| observer.Wait(); |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| NoPromptForSubFrameNavigation) { |
| NavigateToFile("/password/multi_frames.html"); |
| |
| // If you are filling out a password form in one frame and a different frame |
| // navigates, this should not trigger the infobar. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| observer.SetPathToWaitFor("/password/done.html"); |
| std::string fill = |
| "var first_frame = document.getElementById('first_frame');" |
| "var frame_doc = first_frame.contentDocument;" |
| "frame_doc.getElementById('username_field').value = 'temp';" |
| "frame_doc.getElementById('password_field').value = 'random';"; |
| std::string navigate_frame = |
| "var second_iframe = document.getElementById('second_frame');" |
| "second_iframe.contentWindow.location.href = 'done.html';"; |
| |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill)); |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame)); |
| observer.Wait(); |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| PromptAfterSubmitWithSubFrameNavigation) { |
| NavigateToFile("/password/multi_frames.html"); |
| |
| // Make sure that we prompt to save password even if a sub-frame navigation |
| // happens first. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| observer.SetPathToWaitFor("/password/done.html"); |
| std::string navigate_frame = |
| "var second_iframe = document.getElementById('second_frame');" |
| "second_iframe.contentWindow.location.href = 'other.html';"; |
| std::string fill_and_submit = |
| "var first_frame = document.getElementById('first_frame');" |
| "var frame_doc = first_frame.contentDocument;" |
| "frame_doc.getElementById('username_field').value = 'temp';" |
| "frame_doc.getElementById('password_field').value = 'random';" |
| "frame_doc.getElementById('input_submit_button').click();"; |
| |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame)); |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| PasswordManagerBrowserTest, |
| NoPromptForFailedLoginFromMainFrameWithMultiFramesInPage) { |
| NavigateToFile("/password/multi_frames.html"); |
| |
| // Make sure that we don't prompt to save the password for a failed login |
| // from the main frame with multiple frames in the same page. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> 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( |
| PasswordManagerBrowserTest, |
| NoPromptForFailedLoginFromSubFrameWithMultiFramesInPage) { |
| NavigateToFile("/password/multi_frames.html"); |
| |
| // Make sure that we don't prompt to save the password for a failed login |
| // from a sub-frame with multiple frames in the same page. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "var first_frame = document.getElementById('first_frame');" |
| "var frame_doc = first_frame.contentDocument;" |
| "frame_doc.getElementById('username_failed').value = 'temp';" |
| "frame_doc.getElementById('password_failed').value = 'random';" |
| "frame_doc.getElementById('submit_failed').click();" |
| "window.parent.location.href = 'multi_frames.html';"; |
| |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForXHRSubmit) { |
| #if defined(OS_WIN) && defined(USE_ASH) |
| // Disable this test in Metro+Ash for now (http://crbug.com/262796). |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kAshBrowserTests)) |
| return; |
| #endif |
| NavigateToFile("/password/password_xhr_submit.html"); |
| |
| // Verify that we show the save password prompt if a form returns false |
| // in its onsubmit handler but instead logs in/navigates via XHR. |
| // Note that calling 'submit()' on a form with javascript doesn't call |
| // the onsubmit handler, so we click the submit button instead. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementById('username_field').value = 'temp';" |
| "document.getElementById('password_field').value = 'random';" |
| "document.getElementById('submit_button').click()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| PromptForXHRWithoutOnSubmit) { |
| NavigateToFile("/password/password_xhr_submit.html"); |
| |
| // Verify that if XHR navigation occurs and the form is properly filled out, |
| // we try and save the password even though onsubmit hasn't been called. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_navigate = |
| "document.getElementById('username_field').value = 'temp';" |
| "document.getElementById('password_field').value = 'random';" |
| "send_xhr()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate)); |
| observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| PromptForXHRWithNewPasswordsWithoutOnSubmit) { |
| NavigateToFile("/password/password_xhr_submit.html"); |
| |
| // Verify that if XHR navigation occurs and the form is properly filled out, |
| // we try and save the password even though onsubmit hasn't been called. |
| // Specifically verify that the password form saving new passwords is treated |
| // the same as a login form. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_navigate = |
| "document.getElementById('signup_username_field').value = 'temp';" |
| "document.getElementById('signup_password_field').value = 'random';" |
| "document.getElementById('confirmation_password_field').value = 'random';" |
| "send_xhr()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate)); |
| observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptIfLinkClicked) { |
| NavigateToFile("/password/password_form.html"); |
| |
| // Verify that if the user takes a direct action to leave the page, we don't |
| // prompt to save the password even if the form is already filled out. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_click_link = |
| "document.getElementById('username_field').value = 'temp';" |
| "document.getElementById('password_field').value = 'random';" |
| "document.getElementById('link').click();"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_click_link)); |
| observer.Wait(); |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| // TODO(jam): http://crbug.com/350550 |
| #if !defined(OS_WIN) |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| VerifyPasswordGenerationUpload) { |
| // Prevent Autofill requests from actually going over the wire. |
| net::TestURLFetcherFactory factory; |
| // Disable Autofill requesting access to AddressBook data. This causes |
| // the test to hang on Mac. |
| autofill::test::DisableSystemServices(browser()->profile()->GetPrefs()); |
| |
| // Visit a signup form. |
| NavigateToFile("/password/signup_form.html"); |
| |
| // Enter a password and save it. |
| NavigationObserver first_observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementById('other_info').value = 'stuff';" |
| "document.getElementById('username_field').value = 'my_username';" |
| "document.getElementById('password_field').value = 'password';" |
| "document.getElementById('input_submit_button').click()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| |
| first_observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| prompt_observer->Accept(); |
| |
| // Now navigate to a login form that has similar HTML markup. |
| NavigateToFile("/password/password_form.html"); |
| |
| // Simulate a user click to force an autofill of the form's DOM value, not |
| // just the suggested value. |
| content::SimulateMouseClick( |
| WebContents(), 0, blink::WebMouseEvent::ButtonLeft); |
| |
| // The form should be filled with the previously submitted username. |
| std::string get_username = |
| "window.domAutomationController.send(" |
| "document.getElementById('username_field').value);"; |
| std::string actual_username; |
| ASSERT_TRUE(content::ExecuteScriptAndExtractString(RenderViewHost(), |
| get_username, |
| &actual_username)); |
| ASSERT_EQ("my_username", actual_username); |
| |
| // Submit the form and verify that there is no infobar (as the password |
| // has already been saved). |
| NavigationObserver second_observer(WebContents()); |
| scoped_ptr<PromptObserver> second_prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string submit_form = |
| "document.getElementById('input_submit_button').click()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit_form)); |
| second_observer.Wait(); |
| EXPECT_FALSE(second_prompt_observer->IsShowingPrompt()); |
| |
| // Verify that we sent two pings to Autofill. One vote for of PASSWORD for |
| // the current form, and one vote for ACCOUNT_CREATION_PASSWORD on the |
| // original form since it has more than 2 text input fields and was used for |
| // the first time on a different form. |
| base::HistogramBase* upload_histogram = |
| base::StatisticsRecorder::FindHistogram( |
| "PasswordGeneration.UploadStarted"); |
| ASSERT_TRUE(upload_histogram); |
| scoped_ptr<base::HistogramSamples> snapshot = |
| upload_histogram->SnapshotSamples(); |
| EXPECT_EQ(0, snapshot->GetCount(0 /* failure */)); |
| EXPECT_EQ(2, snapshot->GetCount(1 /* success */)); |
| } |
| #endif |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForSubmitFromIframe) { |
| NavigateToFile("/password/password_submit_from_iframe.html"); |
| |
| // Submit a form in an iframe, then cause the whole page to navigate without a |
| // user gesture. We expect the save password prompt to be shown here, because |
| // some pages use such iframes for login forms. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "var iframe = document.getElementById('test_iframe');" |
| "var iframe_doc = iframe.contentDocument;" |
| "iframe_doc.getElementById('username_field').value = 'temp';" |
| "iframe_doc.getElementById('password_field').value = 'random';" |
| "iframe_doc.getElementById('submit_button').click()"; |
| |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| PromptForInputElementWithoutName) { |
| // Check that the prompt is shown for forms where input elements lack the |
| // "name" attribute but the "id" is present. |
| NavigateToFile("/password/password_form.html"); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementById('username_field_no_name').value = 'temp';" |
| "document.getElementById('password_field_no_name').value = 'random';" |
| "document.getElementById('input_submit_button_no_name').click()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| PromptForInputElementWithoutId) { |
| // Check that the prompt is shown for forms where input elements lack the |
| // "id" attribute but the "name" attribute is present. |
| NavigateToFile("/password/password_form.html"); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementsByName('username_field_no_id')[0].value = 'temp';" |
| "document.getElementsByName('password_field_no_id')[0].value = 'random';" |
| "document.getElementsByName('input_submit_button_no_id')[0].click()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| NoPromptForInputElementWithoutIdAndName) { |
| // Check that no prompt is shown for forms where the input fields lack both |
| // the "id" and the "name" attributes. |
| NavigateToFile("/password/password_form.html"); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "var form = document.getElementById('testform_elements_no_id_no_name');" |
| "var username = form.children[0];" |
| "username.value = 'temp';" |
| "var password = form.children[1];" |
| "password.value = 'random';" |
| "form.children[2].click()"; // form.children[2] is the submit button. |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| // Test for checking that no prompt is shown for URLs with file: scheme. |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| NoPromptForFileSchemeURLs) { |
| GURL url = GetFileURL("password_form.html"); |
| ui_test_utils::NavigateToURL(browser(), url); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> 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_FALSE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| NoPromptForLandingPageWithHTTPErrorStatusCode) { |
| // Check that no prompt is shown for forms where the landing page has |
| // HTTP status 404. |
| NavigateToFile("/password/password_form.html"); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementById('username_field_http_error').value = 'temp';" |
| "document.getElementById('password_field_http_error').value = 'random';" |
| "document.getElementById('input_submit_button_http_error').click()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, DeleteFrameBeforeSubmit) { |
| NavigateToFile("/password/multi_frames.html"); |
| |
| NavigationObserver observer(WebContents()); |
| // Make sure we save some password info from an iframe and then destroy it. |
| std::string save_and_remove = |
| "var first_frame = document.getElementById('first_frame');" |
| "var frame_doc = first_frame.contentDocument;" |
| "frame_doc.getElementById('username_field').value = 'temp';" |
| "frame_doc.getElementById('password_field').value = 'random';" |
| "frame_doc.getElementById('input_submit_button').click();" |
| "first_frame.parentNode.removeChild(first_frame);"; |
| // Submit from the main frame, but without navigating through the onsubmit |
| // handler. |
| std::string navigate_frame = |
| "document.getElementById('username_field').value = 'temp';" |
| "document.getElementById('password_field').value = 'random';" |
| "document.getElementById('input_submit_button').click();" |
| "window.location.href = 'done.html';"; |
| |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), save_and_remove)); |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame)); |
| observer.Wait(); |
| // The only thing we check here is that there is no use-after-free reported. |
| } |
| |
| // Disabled on Windows due to flakiness: http://crbug.com/346297 |
| #if defined(OS_WIN) |
| #define MAYBE_PasswordValueAccessible DISABLED_PasswordValueAccessible |
| #else |
| #define MAYBE_PasswordValueAccessible PasswordValueAccessible |
| #endif |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| MAYBE_PasswordValueAccessible) { |
| NavigateToFile("/password/form_and_link.html"); |
| |
| // Click on a link to open a new tab, then switch back to the first one. |
| EXPECT_EQ(1, browser()->tab_strip_model()->count()); |
| std::string click = |
| "document.getElementById('testlink').click();"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), click)); |
| EXPECT_EQ(2, browser()->tab_strip_model()->count()); |
| browser()->tab_strip_model()->ActivateTabAt(0, false); |
| |
| // Fill in the credentials, and make sure they are saved. |
| NavigationObserver form_submit_observer(WebContents()); |
| scoped_ptr<PromptObserver> 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)); |
| form_submit_observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| prompt_observer->Accept(); |
| |
| // Reload the original page to have the saved credentials autofilled. |
| NavigationObserver reload_observer(WebContents()); |
| NavigateToFile("/password/form_and_link.html"); |
| reload_observer.Wait(); |
| |
| // Wait until the username is filled, to make sure autofill kicked in. |
| WaitForElementValue("username_field", "temp"); |
| // Now check that the password is not accessible yet. |
| CheckElementValue("password_field", ""); |
| // Let the user interact with the page. |
| content::SimulateMouseClickAt( |
| WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(1, 1)); |
| // Wait until that interaction causes the password value to be revealed. |
| WaitForElementValue("password_field", "random"); |
| // And check that after the side-effects of the interaction took place, the |
| // username value stays the same. |
| CheckElementValue("username_field", "temp"); |
| } |
| |
| // The following test is limited to Aura, because |
| // RenderWidgetHostViewGuest::ProcessAckedTouchEvent is, and |
| // ProcessAckedTouchEvent is what triggers the translation of touch events to |
| // gesture events. |
| #if defined(USE_AURA) |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| PasswordValueAccessibleOnSubmit) { |
| NavigateToFile("/password/form_and_link.html"); |
| |
| // Fill in the credentials, and make sure they are saved. |
| NavigationObserver form_submit_observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementById('username_field').value = 'temp';" |
| "document.getElementById('password_field').value = 'random_secret';" |
| "document.getElementById('input_submit_button').click();"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| form_submit_observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| prompt_observer->Accept(); |
| |
| // Reload the original page to have the saved credentials autofilled. |
| NavigationObserver reload_observer(WebContents()); |
| NavigateToFile("/password/form_and_link.html"); |
| reload_observer.Wait(); |
| |
| NavigationObserver submit_observer(WebContents()); |
| // Submit the form via a tap on the submit button. The button is placed at 0, |
| // 100, and has height 300 and width 700. |
| content::SimulateTapAt(WebContents(), gfx::Point(350, 250)); |
| submit_observer.Wait(); |
| std::string query = WebContents()->GetURL().query(); |
| EXPECT_NE(std::string::npos, query.find("random_secret")) << query; |
| } |
| #endif |
| |
| // Test fix for crbug.com/338650. |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| DontPromptForPasswordFormWithDefaultValue) { |
| NavigateToFile("/password/password_form_with_default_value.html"); |
| |
| // Don't prompt if we navigate away even if there is a password value since |
| // it's not coming from the user. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| NavigateToFile("/password/done.html"); |
| observer.Wait(); |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| PromptWhenEnableAutomaticPasswordSavingSwitchIsNotSet) { |
| NavigateToFile("/password/password_form.html"); |
| |
| // Fill a form and submit through a <input type="submit"> button. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> 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(PasswordManagerBrowserTest, |
| DontPromptWhenEnableAutomaticPasswordSavingSwitchIsSet) { |
| password_manager::TestPasswordStore* password_store = |
| static_cast<password_manager::TestPasswordStore*>( |
| PasswordStoreFactory::GetForProfile( |
| browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS).get()); |
| |
| EXPECT_TRUE(password_store->IsEmpty()); |
| |
| NavigateToFile("/password/password_form.html"); |
| |
| // Add the enable-automatic-password-saving switch. |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| password_manager::switches::kEnableAutomaticPasswordSaving); |
| |
| // Fill a form and submit through a <input type="submit"> button. |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| // Make sure that the only passwords saved are the auto-saved ones. |
| 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(); |
| if (chrome::VersionInfo::GetChannel() == |
| chrome::VersionInfo::CHANNEL_UNKNOWN) { |
| // Passwords getting auto-saved, no prompt. |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| EXPECT_FALSE(password_store->IsEmpty()); |
| } else { |
| // Prompt shown, and no passwords saved automatically. |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| EXPECT_TRUE(password_store->IsEmpty()); |
| } |
| } |
| |
| // Test fix for crbug.com/368690. |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptWhenReloading) { |
| NavigateToFile("/password/password_form.html"); |
| |
| std::string fill = |
| "document.getElementById('username_redirect').value = 'temp';" |
| "document.getElementById('password_redirect').value = 'random';"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill)); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| GURL url = embedded_test_server()->GetURL("/password/password_form.html"); |
| chrome::NavigateParams params(browser(), url, |
| ui::PAGE_TRANSITION_RELOAD); |
| ui_test_utils::NavigateToURL(¶ms); |
| observer.Wait(); |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| // Test that if a form gets dynamically added between the form parsing and |
| // rendering, and while the main frame still loads, it still is registered, and |
| // thus saving passwords from it works. |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| FormsAddedBetweenParsingAndRendering) { |
| NavigateToFile("/password/between_parsing_and_rendering.html"); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string submit = |
| "document.getElementById('username').value = 'temp';" |
| "document.getElementById('password').value = 'random';" |
| "document.getElementById('submit-button').click();"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit)); |
| observer.Wait(); |
| |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| // Test that if there was no previous page load then the PasswordManagerDriver |
| // does not think that there were SSL errors on the current page. The test opens |
| // a new tab with a URL for which the embedded test server issues a basic auth |
| // challenge. |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoLastLoadGoodLastLoad) { |
| // Teach the embedded server to handle requests by issuing the basic auth |
| // challenge. |
| embedded_test_server()->RegisterRequestHandler( |
| base::Bind(&HandleTestAuthRequest)); |
| |
| LoginPromptBrowserTestObserver login_observer; |
| // We need to register to all sources, because the navigation observer we are |
| // interested in is for a new tab to be opened, and thus does not exist yet. |
| login_observer.Register(content::NotificationService::AllSources()); |
| |
| password_manager::TestPasswordStore* password_store = |
| static_cast<password_manager::TestPasswordStore*>( |
| PasswordStoreFactory::GetForProfile( |
| browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS).get()); |
| EXPECT_TRUE(password_store->IsEmpty()); |
| |
| // Navigate to a page requiring HTTP auth. Wait for the tab to get the correct |
| // WebContents, but don't wait for navigation, which only finishes after |
| // authentication. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), |
| embedded_test_server()->GetURL("/basic_auth"), |
| NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB); |
| |
| content::NavigationController* nav_controller = |
| &WebContents()->GetController(); |
| NavigationObserver nav_observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| WindowedAuthNeededObserver auth_needed_observer(nav_controller); |
| auth_needed_observer.Wait(); |
| |
| WindowedAuthSuppliedObserver auth_supplied_observer(nav_controller); |
| // Offer valid credentials on the auth challenge. |
| ASSERT_EQ(1u, login_observer.handlers().size()); |
| LoginHandler* handler = *login_observer.handlers().begin(); |
| ASSERT_TRUE(handler); |
| // Any username/password will work. |
| handler->SetAuth(base::UTF8ToUTF16("user"), base::UTF8ToUTF16("pwd")); |
| auth_supplied_observer.Wait(); |
| |
| // The password manager should be working correctly. |
| nav_observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| prompt_observer->Accept(); |
| |
| // Spin the message loop to make sure the password store had a chance to save |
| // the password. |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| EXPECT_FALSE(password_store->IsEmpty()); |
| } |
| |
| // In some situations, multiple PasswordFormManager instances from |
| // PasswordManager::pending_login_managers_ would match (via DoesManage) a form |
| // to be provisionally saved. One of them might be a complete match, the other |
| // all-but-action match. Normally, the former should be preferred, but if the |
| // former has not finished matching, and the latter has, the latter should be |
| // used (otherwise we'd give up even though we could have saved the password). |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| PreferPasswordFormManagerWhichFinishedMatching) { |
| NavigateToFile("/password/create_form_copy_on_submit.html"); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string submit = |
| "document.getElementById('username').value = 'overwrite_me';" |
| "document.getElementById('password').value = 'random';" |
| "document.getElementById('non-form-button').click();"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit)); |
| observer.Wait(); |
| |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| // Test that if login fails and content server pushes a different login form |
| // with action URL having different schemes. Heuristic shall be able |
| // identify such cases and *shall not* prompt to save incorrect password. |
| IN_PROC_BROWSER_TEST_F( |
| PasswordManagerBrowserTest, |
| NoPromptForLoginFailedAndServerPushSeperateLoginForm_HttpToHttps) { |
| std::string path = |
| "/password/separate_login_form_with_onload_submit_script.html"; |
| GURL http_url(embedded_test_server()->GetURL(path)); |
| ASSERT_TRUE(http_url.SchemeIs(url::kHttpScheme)); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| ui_test_utils::NavigateToURL(browser(), http_url); |
| |
| observer.SetPathToWaitFor("/password/done_and_separate_login_form.html"); |
| observer.Wait(); |
| |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| PasswordManagerBrowserTest, |
| NoPromptForLoginFailedAndServerPushSeperateLoginForm_HttpsToHttp) { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kAllowRunningInsecureContent); |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kIgnoreCertificateErrors); |
| const base::FilePath::CharType kDocRoot[] = |
| FILE_PATH_LITERAL("chrome/test/data"); |
| net::SpawnedTestServer https_test_server( |
| net::SpawnedTestServer::TYPE_HTTPS, |
| net::SpawnedTestServer::SSLOptions( |
| net::SpawnedTestServer::SSLOptions::CERT_OK), |
| base::FilePath(kDocRoot)); |
| ASSERT_TRUE(https_test_server.Start()); |
| |
| // This test case cannot inject the scripts via content::ExecuteScript() in |
| // files served through HTTPS. Therefore the scripts are made part of the HTML |
| // site and executed on load. |
| std::string path = |
| "password/separate_login_form_with_onload_submit_script.html"; |
| GURL https_url(https_test_server.GetURL(path)); |
| ASSERT_TRUE(https_url.SchemeIs(url::kHttpsScheme)); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| ui_test_utils::NavigateToURL(browser(), https_url); |
| |
| observer.SetPathToWaitFor("/password/done_and_separate_login_form.html"); |
| observer.Wait(); |
| |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| PromptWhenPasswordFormWithoutUsernameFieldSubmitted) { |
| password_manager::TestPasswordStore* password_store = |
| static_cast<password_manager::TestPasswordStore*>( |
| PasswordStoreFactory::GetForProfile( |
| browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS).get()); |
| |
| EXPECT_TRUE(password_store->IsEmpty()); |
| |
| NavigateToFile("/password/form_with_only_password_field.html"); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string submit = |
| "document.getElementById('password').value = 'password';" |
| "document.getElementById('submit-button').click();"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit)); |
| observer.Wait(); |
| |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| prompt_observer->Accept(); |
| |
| // Spin the message loop to make sure the password store had a chance to save |
| // the password. |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| EXPECT_FALSE(password_store->IsEmpty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| AutofillSuggetionsForPasswordFormWithoutUsernameField) { |
| password_manager::TestPasswordStore* password_store = |
| static_cast<password_manager::TestPasswordStore*>( |
| PasswordStoreFactory::GetForProfile( |
| browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS).get()); |
| |
| EXPECT_TRUE(password_store->IsEmpty()); |
| |
| // Password form without username-field. |
| NavigateToFile("/password/form_with_only_password_field.html"); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string submit = |
| "document.getElementById('password').value = 'mypassword';" |
| "document.getElementById('submit-button').click();"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit)); |
| observer.Wait(); |
| |
| prompt_observer->Accept(); |
| |
| // Spin the message loop to make sure the password store had a chance to save |
| // the password. |
| base::RunLoop run_loop; |
| run_loop.RunUntilIdle(); |
| EXPECT_FALSE(password_store->IsEmpty()); |
| |
| // Now, navigate to same html password form and verify whether password is |
| // autofilled. |
| NavigateToFile("/password/form_with_only_password_field.html"); |
| |
| // Let the user interact with the page, so that DOM gets modification events, |
| // needed for autofilling fields. |
| content::SimulateMouseClickAt( |
| WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(1, 1)); |
| |
| // Wait until that interaction causes the password value to be revealed. |
| WaitForElementValue("password", "mypassword"); |
| } |
| |
| // Test that if a form gets autofilled, then it gets autofilled on re-creation |
| // as well. |
| // TODO(vabr): This is flaky everywhere. http://crbug.com/442704 |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| DISABLED_ReCreatedFormsGetFilled) { |
| NavigateToFile("/password/dynamic_password_form.html"); |
| |
| // Fill in the credentials, and make sure they are saved. |
| NavigationObserver form_submit_observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string create_fill_and_submit = |
| "document.getElementById('create_form_button').click();" |
| "window.setTimeout(function() {" |
| " var form = document.getElementById('dynamic_form_id');" |
| " form.username.value = 'temp';" |
| " form.password.value = 'random';" |
| " form.submit();" |
| "}, 0)"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), create_fill_and_submit)); |
| form_submit_observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| prompt_observer->Accept(); |
| |
| // Reload the original page to have the saved credentials autofilled. |
| NavigationObserver reload_observer(WebContents()); |
| NavigateToFile("/password/dynamic_password_form.html"); |
| reload_observer.Wait(); |
| std::string create_form = |
| "document.getElementById('create_form_button').click();"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), create_form)); |
| // Wait until the username is filled, to make sure autofill kicked in. |
| WaitForElementValue("username_id", "temp"); |
| |
| // Now the form gets deleted and created again. It should get autofilled |
| // again. |
| std::string delete_form = |
| "var form = document.getElementById('dynamic_form_id');" |
| "form.parentNode.removeChild(form);"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), delete_form)); |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), create_form)); |
| WaitForElementValue("username_id", "temp"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForPushState) { |
| ActivateHistoryPushState(); |
| NavigateToFile("/password/password_push_state.html"); |
| |
| // Verify that we show the save password prompt if 'history.pushState()' |
| // is called after form submission is suppressed by, for example, calling |
| // preventDefault in a form's submit event handler. |
| // Note that calling 'submit()' on a form with javascript doesn't call |
| // the onsubmit handler, so we click the submit button instead. |
| NavigationObserver observer(WebContents()); |
| observer.SetQuitOnEntryCommitted(true); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementById('username_field').value = 'temp';" |
| "document.getElementById('password_field').value = 'random';" |
| "document.getElementById('submit_button').click()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| InFrameNavigationDoesNotClearPopupState) { |
| // Mock out the AutofillClient so we know how long to wait. Unfortunately |
| // there isn't otherwise a good even to wait on to verify that the popup |
| // would have been shown. |
| password_manager::ContentPasswordManagerDriverFactory* driver_factory = |
| password_manager::ContentPasswordManagerDriverFactory::FromWebContents( |
| WebContents()); |
| ObservingAutofillClient observing_autofill_client; |
| driver_factory->TestingSetDriverForFrame( |
| RenderViewHost()->GetMainFrame(), |
| make_scoped_ptr(new password_manager::ContentPasswordManagerDriver( |
| RenderViewHost()->GetMainFrame(), |
| ChromePasswordManagerClient::FromWebContents(WebContents()), |
| &observing_autofill_client))); |
| |
| NavigateToFile("/password/password_form.html"); |
| |
| NavigationObserver form_submit_observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill = |
| "document.getElementById('username_field').value = 'temp';" |
| "document.getElementById('password_field').value = 'random123';" |
| "document.getElementById('input_submit_button').click();"; |
| |
| // Save credentials for the site. |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill)); |
| form_submit_observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| prompt_observer->Accept(); |
| |
| NavigateToFile("/password/password_form.html"); |
| ASSERT_TRUE(content::ExecuteScript( |
| RenderViewHost(), |
| "var usernameRect = document.getElementById('username_field')" |
| ".getBoundingClientRect();")); |
| |
| // Trigger in page navigation. |
| std::string in_page_navigate = "location.hash = '#blah';"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), in_page_navigate)); |
| |
| // Click on the username field to display the popup. |
| int top; |
| ASSERT_TRUE(content::ExecuteScriptAndExtractInt( |
| RenderViewHost(), |
| "window.domAutomationController.send(usernameRect.top);", |
| &top)); |
| int left; |
| ASSERT_TRUE(content::ExecuteScriptAndExtractInt( |
| RenderViewHost(), |
| "window.domAutomationController.send(usernameRect.left);", |
| &left)); |
| |
| content::SimulateMouseClickAt( |
| WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(left + 1, |
| top + 1)); |
| // Make sure the popup would be shown. |
| observing_autofill_client.Wait(); |
| } |
| |
| // Passwords from change password forms should only be offered for saving when |
| // it is certain that the username is correct. |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, ChangePwdCorrect) { |
| NavigateToFile("/password/password_form.html"); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementById('mark_chg_username_field').value = 'temp';" |
| "document.getElementById('mark_chg_password_field').value = 'random';" |
| "document.getElementById('mark_chg_new_password_1').value = 'random1';" |
| "document.getElementById('mark_chg_new_password_2').value = 'random1';" |
| "document.getElementById('mark_chg_submit_button').click()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, ChangePwdIncorrect) { |
| NavigateToFile("/password/password_form.html"); |
| |
| NavigationObserver observer(WebContents()); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementById('chg_not_username_field').value = 'temp';" |
| "document.getElementById('chg_password_field').value = 'random';" |
| "document.getElementById('chg_new_password_1').value = 'random1';" |
| "document.getElementById('chg_new_password_2').value = 'random1';" |
| "document.getElementById('chg_submit_button').click()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| // As the two ChangePwd* tests above, only with submitting through |
| // history.pushState(). |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, ChangePwdPushStateCorrect) { |
| ActivateHistoryPushState(); |
| NavigateToFile("/password/password_push_state.html"); |
| |
| NavigationObserver observer(WebContents()); |
| observer.SetQuitOnEntryCommitted(true); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementById('mark_chg_username_field').value = 'temp';" |
| "document.getElementById('mark_chg_password_field').value = 'random';" |
| "document.getElementById('mark_chg_new_password_1').value = 'random1';" |
| "document.getElementById('mark_chg_new_password_2').value = 'random1';" |
| "document.getElementById('mark_chg_submit_button').click()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_TRUE(prompt_observer->IsShowingPrompt()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, |
| ChangePwdPushStateIncorrect) { |
| ActivateHistoryPushState(); |
| NavigateToFile("/password/password_push_state.html"); |
| |
| NavigationObserver observer(WebContents()); |
| observer.SetQuitOnEntryCommitted(true); |
| scoped_ptr<PromptObserver> prompt_observer( |
| PromptObserver::Create(WebContents())); |
| std::string fill_and_submit = |
| "document.getElementById('chg_not_username_field').value = 'temp';" |
| "document.getElementById('chg_password_field').value = 'random';" |
| "document.getElementById('chg_new_password_1').value = 'random1';" |
| "document.getElementById('chg_new_password_2').value = 'random1';" |
| "document.getElementById('chg_submit_button').click()"; |
| ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit)); |
| observer.Wait(); |
| EXPECT_FALSE(prompt_observer->IsShowingPrompt()); |
| } |