blob: 61bc94eacc9d0baa6a0e6cffc8acb16ed5660d55 [file] [log] [blame]
// 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 "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/infobars/confirm_infobar_delegate.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/password_manager/password_store_factory.h"
#include "chrome/browser/password_manager/test_password_store.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.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 "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.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/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/base/keycodes/keyboard_codes.h"
// NavigationObserver ---------------------------------------------------------
namespace {
class NavigationObserver : public content::NotificationObserver,
public content::WebContentsObserver {
public:
explicit NavigationObserver(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
message_loop_runner_(new content::MessageLoopRunner),
infobar_shown_(false),
infobar_service_(InfoBarService::FromWebContents(web_contents)) {
registrar_.Add(this,
chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
content::Source<InfoBarService>(infobar_service_));
}
virtual ~NavigationObserver() {}
// content::NotificationObserver:
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) OVERRIDE {
infobar_service_->infobar_at(0)->AsConfirmInfoBarDelegate()->Accept();
infobar_shown_ = true;
}
// content::WebContentsObserver:
virtual void DidFinishLoad(
int64 frame_id,
const GURL& validated_url,
bool is_main_frame,
content::RenderViewHost* render_view_host) OVERRIDE {
message_loop_runner_->Quit();
}
bool infobar_shown() const { return infobar_shown_; }
void Wait() {
message_loop_runner_->Run();
}
private:
scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
bool infobar_shown_;
content::NotificationRegistrar registrar_;
InfoBarService* infobar_service_;
DISALLOW_COPY_AND_ASSIGN(NavigationObserver);
};
} // namespace
// PasswordManagerBrowserTest -------------------------------------------------
class PasswordManagerBrowserTest : public InProcessBrowserTest {
public:
PasswordManagerBrowserTest() {}
virtual ~PasswordManagerBrowserTest() {}
// InProcessBrowserTest:
virtual 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(), &TestPasswordStore::Create);
}
protected:
content::WebContents* WebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
content::RenderViewHost* RenderViewHost() {
return WebContents()->GetRenderViewHost();
}
private:
DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest);
};
// Actual tests ---------------------------------------------------------------
// This test was a bit flaky <http://crbug.com/276597>.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
DISABLED_PromptForNormalSubmit) {
ASSERT_TRUE(test_server()->Start());
GURL url = test_server()->GetURL("files/password/password_form.html");
ui_test_utils::NavigateToURL(browser(), url);
// Fill a form and submit through a <input type="submit"> button. Nothing
// special.
NavigationObserver observer(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(observer.infobar_shown());
}
// This test was a bit flaky <http://crbug.com/276597>.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
DISABLED_PromptForSubmitUsingJavaScript) {
ASSERT_TRUE(test_server()->Start());
GURL url = test_server()->GetURL("files/password/password_form.html");
ui_test_utils::NavigateToURL(browser(), url);
// 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());
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(observer.infobar_shown());
}
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForNavigation) {
ASSERT_TRUE(test_server()->Start());
GURL url = test_server()->GetURL("files/password/password_form.html");
ui_test_utils::NavigateToURL(browser(), url);
// Don't fill the password form, just navigate away. Shouldn't prompt.
NavigationObserver observer(WebContents());
ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
"window.location.href = 'done.html';"));
observer.Wait();
EXPECT_FALSE(observer.infobar_shown());
}
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
NoPromptForSubFrameNavigation) {
ASSERT_TRUE(test_server()->Start());
GURL url = test_server()->GetURL("files/password/multi_frames.html");
ui_test_utils::NavigateToURL(browser(), url);
// 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());
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(observer.infobar_shown());
}
// This test was a bit flaky <http://crbug.com/276597>.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
DISABLED_PromptAfterSubmitWithSubFrameNavigation) {
ASSERT_TRUE(test_server()->Start());
GURL url = test_server()->GetURL("files/password/multi_frames.html");
ui_test_utils::NavigateToURL(browser(), url);
// Make sure that we prompt to save password even if a sub-frame navigation
// happens first.
NavigationObserver observer(WebContents());
std::string navigate_frame =
"var second_iframe = document.getElementById('second_frame');"
"second_iframe.contentWindow.location.href = 'done.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(observer.infobar_shown());
}
// This test was a bit flaky <http://crbug.com/276597>.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
DISABLED_PromptForXHRSubmit) {
#if defined(OS_WIN) && defined(USE_ASH)
// Disable this test in Metro+Ash for now (http://crbug.com/262796).
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
return;
#endif
ASSERT_TRUE(test_server()->Start());
GURL url = test_server()->GetURL("files/password/password_xhr_submit.html");
ui_test_utils::NavigateToURL(browser(), url);
// 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());
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(observer.infobar_shown());
}
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForOtherXHR) {
ASSERT_TRUE(test_server()->Start());
GURL url = test_server()->GetURL("files/password/password_xhr_submit.html");
ui_test_utils::NavigateToURL(browser(), url);
// Verify that if random XHR navigation occurs, we don't try and save the
// password.
//
// We may want to change this functionality in the future to account for
// cases where the element that users click on isn't a submit button.
NavigationObserver observer(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_FALSE(observer.infobar_shown());
}