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.
7 #include "base/command_line.h"
8 #include "base/metrics/histogram_samples.h"
9 #include "base/metrics/statistics_recorder.h"
10 #include "base/run_loop.h"
11 #include "base/stl_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/infobars/infobar_service.h"
16 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
17 #include "chrome/browser/password_manager/password_store_factory.h"
18 #include "chrome/browser/password_manager/test_password_store_service.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/login/login_prompt.h"
21 #include "chrome/browser/ui/login/login_prompt_test_utils.h"
22 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
23 #include "chrome/browser/ui/tabs/tab_strip_model.h"
24 #include "chrome/common/chrome_version_info.h"
25 #include "chrome/test/base/in_process_browser_test.h"
26 #include "chrome/test/base/test_switches.h"
27 #include "chrome/test/base/ui_test_utils.h"
28 #include "components/autofill/core/browser/autofill_test_utils.h"
29 #include "components/infobars/core/confirm_infobar_delegate.h"
30 #include "components/infobars/core/infobar.h"
31 #include "components/infobars/core/infobar_manager.h"
32 #include "components/password_manager/core/browser/test_password_store.h"
33 #include "components/password_manager/core/common/password_manager_switches.h"
34 #include "content/public/browser/navigation_controller.h"
35 #include "content/public/browser/notification_service.h"
36 #include "content/public/browser/render_frame_host.h"
37 #include "content/public/browser/render_view_host.h"
38 #include "content/public/browser/web_contents.h"
39 #include "content/public/browser/web_contents_observer.h"
40 #include "content/public/test/browser_test_utils.h"
41 #include "content/public/test/test_utils.h"
42 #include "net/test/embedded_test_server/embedded_test_server.h"
43 #include "net/test/embedded_test_server/http_request.h"
44 #include "net/test/embedded_test_server/http_response.h"
45 #include "net/url_request/test_url_fetcher_factory.h"
46 #include "testing/gmock/include/gmock/gmock.h"
47 #include "third_party/WebKit/public/web/WebInputEvent.h"
48 #include "ui/events/keycodes/keyboard_codes.h"
49 #include "ui/gfx/geometry/point.h"
52 // NavigationObserver ---------------------------------------------------------
56 // Observer that waits for navigation to complete and for the password infobar
58 class NavigationObserver
: public content::WebContentsObserver
{
60 explicit NavigationObserver(content::WebContents
* web_contents
)
61 : content::WebContentsObserver(web_contents
),
62 message_loop_runner_(new content::MessageLoopRunner
) {}
64 virtual ~NavigationObserver() {}
66 // Normally Wait() will not return until a main frame navigation occurs.
67 // If a path is set, Wait() will return after this path has been seen,
68 // regardless of the frame that navigated. Useful for multi-frame pages.
69 void SetPathToWaitFor(const std::string
& path
) {
70 wait_for_path_
= path
;
73 // content::WebContentsObserver:
74 virtual void DidFinishLoad(content::RenderFrameHost
* render_frame_host
,
75 const GURL
& validated_url
) OVERRIDE
{
76 if (!wait_for_path_
.empty()) {
77 if (validated_url
.path() == wait_for_path_
)
78 message_loop_runner_
->Quit();
79 } else if (!render_frame_host
->GetParent()) {
80 message_loop_runner_
->Quit();
84 void Wait() { message_loop_runner_
->Run(); }
87 std::string wait_for_path_
;
88 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
90 DISALLOW_COPY_AND_ASSIGN(NavigationObserver
);
93 // Observes the save password prompt (bubble or infobar) for a specified
94 // WebContents, keeps track of whether or not it is currently shown, and allows
95 // accepting saving passwords through it.
96 class PromptObserver
{
98 virtual ~PromptObserver() {}
100 // Checks if the prompt is being currently shown.
101 virtual bool IsShowingPrompt() const = 0;
103 // Expecting that the prompt is shown, saves the password. Checks that the
104 // prompt is no longer visible afterwards.
105 void Accept() const {
106 EXPECT_TRUE(IsShowingPrompt());
110 // Chooses the right implementation of PromptObserver and creates an instance
112 static scoped_ptr
<PromptObserver
> Create(content::WebContents
* web_contents
);
117 // Accepts the password. The implementation can assume that the prompt is
118 // currently shown, but is required to verify that the prompt is eventually
120 virtual void AcceptImpl() const = 0;
123 DISALLOW_COPY_AND_ASSIGN(PromptObserver
);
126 class InfoBarObserver
: public PromptObserver
,
127 public infobars::InfoBarManager::Observer
{
129 explicit InfoBarObserver(content::WebContents
* web_contents
)
130 : infobar_is_being_shown_(false),
131 infobar_service_(InfoBarService::FromWebContents(web_contents
)) {
132 infobar_service_
->AddObserver(this);
135 virtual ~InfoBarObserver() {
136 if (infobar_service_
)
137 infobar_service_
->RemoveObserver(this);
142 virtual bool IsShowingPrompt() const OVERRIDE
{
143 return infobar_is_being_shown_
;
146 virtual void AcceptImpl() const OVERRIDE
{
147 EXPECT_EQ(1u, infobar_service_
->infobar_count());
148 if (!infobar_service_
->infobar_count())
149 return; // Let the test finish to gather possibly more diagnostics.
151 // ConfirmInfoBarDelegate::Accept returning true means the infobar is
152 // immediately closed. Checking the return value is preferred to testing
153 // IsShowingPrompt() here, for it avoids the delay until the closing
154 // notification is received.
155 EXPECT_TRUE(infobar_service_
->infobar_at(0)
157 ->AsConfirmInfoBarDelegate()
161 // infobars::InfoBarManager::Observer:
162 virtual void OnInfoBarAdded(infobars::InfoBar
* infobar
) OVERRIDE
{
163 infobar_is_being_shown_
= true;
166 virtual void OnInfoBarRemoved(infobars::InfoBar
* infobar
,
167 bool animate
) OVERRIDE
{
168 infobar_is_being_shown_
= false;
171 virtual void OnManagerShuttingDown(
172 infobars::InfoBarManager
* manager
) OVERRIDE
{
173 ASSERT_EQ(infobar_service_
, manager
);
174 infobar_service_
->RemoveObserver(this);
175 infobar_service_
= NULL
;
178 bool infobar_is_being_shown_
;
179 InfoBarService
* infobar_service_
;
181 DISALLOW_COPY_AND_ASSIGN(InfoBarObserver
);
184 class BubbleObserver
: public PromptObserver
{
186 explicit BubbleObserver(content::WebContents
* web_contents
)
188 ManagePasswordsUIController::FromWebContents(web_contents
)) {}
190 virtual ~BubbleObserver() {}
194 virtual bool IsShowingPrompt() const OVERRIDE
{
195 return ui_controller_
->PasswordPendingUserDecision();
198 virtual void AcceptImpl() const OVERRIDE
{
199 ui_controller_
->SavePassword();
200 EXPECT_FALSE(IsShowingPrompt());
203 ManagePasswordsUIController
* const ui_controller_
;
205 DISALLOW_COPY_AND_ASSIGN(BubbleObserver
);
209 scoped_ptr
<PromptObserver
> PromptObserver::Create(
210 content::WebContents
* web_contents
) {
211 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
212 return scoped_ptr
<PromptObserver
>(new BubbleObserver(web_contents
));
214 return scoped_ptr
<PromptObserver
>(new InfoBarObserver(web_contents
));
218 // Handles |request| to "/basic_auth". If "Authorization" header is present,
219 // responds with a non-empty HTTP 200 page (regardless of its value). Otherwise
220 // serves a Basic Auth challenge.
221 scoped_ptr
<net::test_server::HttpResponse
> HandleTestAuthRequest(
222 const net::test_server::HttpRequest
& request
) {
223 if (!StartsWithASCII(request
.relative_url
, "/basic_auth", true))
224 return scoped_ptr
<net::test_server::HttpResponse
>();
226 if (ContainsKey(request
.headers
, "Authorization")) {
227 scoped_ptr
<net::test_server::BasicHttpResponse
> http_response(
228 new net::test_server::BasicHttpResponse
);
229 http_response
->set_code(net::HTTP_OK
);
230 http_response
->set_content("Success!");
231 return http_response
.PassAs
<net::test_server::HttpResponse
>();
233 scoped_ptr
<net::test_server::BasicHttpResponse
> http_response(
234 new net::test_server::BasicHttpResponse
);
235 http_response
->set_code(net::HTTP_UNAUTHORIZED
);
236 http_response
->AddCustomHeader("WWW-Authenticate",
237 "Basic realm=\"test realm\"");
238 return http_response
.PassAs
<net::test_server::HttpResponse
>();
245 // PasswordManagerBrowserTest -------------------------------------------------
247 class PasswordManagerBrowserTest
: public InProcessBrowserTest
{
249 PasswordManagerBrowserTest() {}
250 virtual ~PasswordManagerBrowserTest() {}
252 // InProcessBrowserTest:
253 virtual void SetUpOnMainThread() OVERRIDE
{
254 // Use TestPasswordStore to remove a possible race. Normally the
255 // PasswordStore does its database manipulation on the DB thread, which
256 // creates a possible race during navigation. Specifically the
257 // PasswordManager will ignore any forms in a page if the load from the
258 // PasswordStore has not completed.
259 PasswordStoreFactory::GetInstance()->SetTestingFactory(
260 browser()->profile(), TestPasswordStoreService::Build
);
261 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
262 ASSERT_FALSE(CommandLine::ForCurrentProcess()->HasSwitch(
263 password_manager::switches::kEnableAutomaticPasswordSaving
));
266 virtual void TearDownOnMainThread() OVERRIDE
{
267 ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
271 content::WebContents
* WebContents() {
272 return browser()->tab_strip_model()->GetActiveWebContents();
275 content::RenderViewHost
* RenderViewHost() {
276 return WebContents()->GetRenderViewHost();
279 // Wrapper around ui_test_utils::NavigateToURL that waits until
280 // DidFinishLoad() fires. Normally this function returns after
281 // DidStopLoading(), which caused flakiness as the NavigationObserver
282 // would sometimes see the DidFinishLoad event from a previous navigation and
283 // return immediately.
284 void NavigateToFile(const std::string
& path
) {
285 NavigationObserver
observer(WebContents());
286 GURL url
= embedded_test_server()->GetURL(path
);
287 ui_test_utils::NavigateToURL(browser(), url
);
291 // Waits until the "value" attribute of the HTML element with |element_id| is
292 // equal to |expected_value|. If the current value is not as expected, this
293 // waits until the "change" event is fired for the element. This also
294 // guarantees that once the real value matches the expected, the JavaScript
295 // event loop is spun to allow all other possible events to take place.
296 void WaitForElementValue(const std::string
& element_id
,
297 const std::string
& expected_value
);
298 // Checks that the current "value" attribute of the HTML element with
299 // |element_id| is equal to |expected_value|.
300 void CheckElementValue(const std::string
& element_id
,
301 const std::string
& expected_value
);
304 DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest
);
307 void PasswordManagerBrowserTest::WaitForElementValue(
308 const std::string
& element_id
,
309 const std::string
& expected_value
) {
310 enum ReturnCodes
{ // Possible results of the JavaScript code.
312 RETURN_CODE_NO_ELEMENT
,
313 RETURN_CODE_WRONG_VALUE
,
316 const std::string value_check_function
= base::StringPrintf(
317 "function valueCheck() {"
318 " var element = document.getElementById('%s');"
319 " return element && element.value == '%s';"
322 expected_value
.c_str());
323 const std::string script
=
324 value_check_function
+
326 "if (valueCheck()) {"
327 " /* Spin the event loop with setTimeout. */"
328 " setTimeout(window.domAutomationController.send(%d), 0);"
330 " var element = document.getElementById('%s');"
332 " window.domAutomationController.send(%d);"
333 " element.onchange = function() {"
334 " if (valueCheck()) {"
335 " /* Spin the event loop with setTimeout. */"
336 " setTimeout(window.domAutomationController.send(%d), 0);"
338 " window.domAutomationController.send(%d);"
344 RETURN_CODE_NO_ELEMENT
,
346 RETURN_CODE_WRONG_VALUE
);
347 int return_value
= RETURN_CODE_INVALID
;
348 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
349 RenderViewHost(), script
, &return_value
));
350 EXPECT_EQ(RETURN_CODE_OK
, return_value
)
351 << "element_id = " << element_id
352 << ", expected_value = " << expected_value
;
355 void PasswordManagerBrowserTest::CheckElementValue(
356 const std::string
& element_id
,
357 const std::string
& expected_value
) {
358 const std::string value_check_script
= base::StringPrintf(
359 "var element = document.getElementById('%s');"
360 "window.domAutomationController.send(element && element.value == '%s');",
362 expected_value
.c_str());
363 bool return_value
= false;
364 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
365 RenderViewHost(), value_check_script
, &return_value
));
366 EXPECT_TRUE(return_value
) << "element_id = " << element_id
367 << ", expected_value = " << expected_value
;
370 // Actual tests ---------------------------------------------------------------
371 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
372 PromptForNormalSubmit
) {
373 NavigateToFile("/password/password_form.html");
375 // Fill a form and submit through a <input type="submit"> button. Nothing
377 NavigationObserver
observer(WebContents());
378 scoped_ptr
<PromptObserver
> prompt_observer(
379 PromptObserver::Create(WebContents()));
380 std::string fill_and_submit
=
381 "document.getElementById('username_field').value = 'temp';"
382 "document.getElementById('password_field').value = 'random';"
383 "document.getElementById('input_submit_button').click()";
384 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
386 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
389 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
390 PromptForSubmitWithInPageNavigation
) {
391 NavigateToFile("/password/password_navigate_before_submit.html");
393 // Fill a form and submit through a <input type="submit"> button. Nothing
394 // special. The form does an in-page navigation before submitting.
395 NavigationObserver
observer(WebContents());
396 scoped_ptr
<PromptObserver
> prompt_observer(
397 PromptObserver::Create(WebContents()));
398 std::string fill_and_submit
=
399 "document.getElementById('username_field').value = 'temp';"
400 "document.getElementById('password_field').value = 'random';"
401 "document.getElementById('input_submit_button').click()";
402 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
404 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
407 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
408 LoginSuccessWithUnrelatedForm
) {
409 // Log in, see a form on the landing page. That form is not related to the
410 // login form (=has a different action), so we should offer saving the
412 NavigateToFile("/password/password_form.html");
414 NavigationObserver
observer(WebContents());
415 scoped_ptr
<PromptObserver
> prompt_observer(
416 PromptObserver::Create(WebContents()));
417 std::string fill_and_submit
=
418 "document.getElementById('username_unrelated').value = 'temp';"
419 "document.getElementById('password_unrelated').value = 'random';"
420 "document.getElementById('submit_unrelated').click()";
421 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
423 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
426 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, LoginFailed
) {
427 // Log in, see a form on the landing page. That form is not related to the
428 // login form (=has a different action), so we should offer saving the
430 NavigateToFile("/password/password_form.html");
432 NavigationObserver
observer(WebContents());
433 scoped_ptr
<PromptObserver
> prompt_observer(
434 PromptObserver::Create(WebContents()));
435 std::string fill_and_submit
=
436 "document.getElementById('username_failed').value = 'temp';"
437 "document.getElementById('password_failed').value = 'random';"
438 "document.getElementById('submit_failed').click()";
439 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
441 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
444 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, Redirects
) {
445 NavigateToFile("/password/password_form.html");
447 // Fill a form and submit through a <input type="submit"> button. The form
448 // points to a redirection page.
449 NavigationObserver
observer(WebContents());
450 scoped_ptr
<PromptObserver
> prompt_observer(
451 PromptObserver::Create(WebContents()));
452 std::string fill_and_submit
=
453 "document.getElementById('username_redirect').value = 'temp';"
454 "document.getElementById('password_redirect').value = 'random';"
455 "document.getElementById('submit_redirect').click()";
456 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
458 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
460 // The redirection page now redirects via Javascript. We check that the
462 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
463 "window.location.href = 'done.html';"));
465 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
468 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
469 PromptForSubmitUsingJavaScript
) {
470 NavigateToFile("/password/password_form.html");
472 // Fill a form and submit using <button> that calls submit() on the form.
473 // This should work regardless of the type of element, as long as submit() is
475 NavigationObserver
observer(WebContents());
476 scoped_ptr
<PromptObserver
> prompt_observer(
477 PromptObserver::Create(WebContents()));
478 std::string fill_and_submit
=
479 "document.getElementById('username_field').value = 'temp';"
480 "document.getElementById('password_field').value = 'random';"
481 "document.getElementById('submit_button').click()";
482 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
484 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
487 // Flaky: crbug.com/301547, observed on win and mac. Probably happens on all
489 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
490 DISABLED_PromptForDynamicForm
) {
491 NavigateToFile("/password/dynamic_password_form.html");
493 // Fill the dynamic password form and submit.
494 NavigationObserver
observer(WebContents());
495 scoped_ptr
<PromptObserver
> prompt_observer(
496 PromptObserver::Create(WebContents()));
497 std::string fill_and_submit
=
498 "document.getElementById('create_form_button').click();"
499 "window.setTimeout(function() {"
500 " document.dynamic_form.username.value = 'tempro';"
501 " document.dynamic_form.password.value = 'random';"
502 " document.dynamic_form.submit();"
504 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
506 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
509 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, NoPromptForNavigation
) {
510 NavigateToFile("/password/password_form.html");
512 // Don't fill the password form, just navigate away. Shouldn't prompt.
513 NavigationObserver
observer(WebContents());
514 scoped_ptr
<PromptObserver
> prompt_observer(
515 PromptObserver::Create(WebContents()));
516 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
517 "window.location.href = 'done.html';"));
519 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
522 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
523 NoPromptForSubFrameNavigation
) {
524 NavigateToFile("/password/multi_frames.html");
526 // If you are filling out a password form in one frame and a different frame
527 // navigates, this should not trigger the infobar.
528 NavigationObserver
observer(WebContents());
529 scoped_ptr
<PromptObserver
> prompt_observer(
530 PromptObserver::Create(WebContents()));
531 observer
.SetPathToWaitFor("/password/done.html");
533 "var first_frame = document.getElementById('first_frame');"
534 "var frame_doc = first_frame.contentDocument;"
535 "frame_doc.getElementById('username_field').value = 'temp';"
536 "frame_doc.getElementById('password_field').value = 'random';";
537 std::string navigate_frame
=
538 "var second_iframe = document.getElementById('second_frame');"
539 "second_iframe.contentWindow.location.href = 'done.html';";
541 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill
));
542 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame
));
544 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
547 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
548 PromptAfterSubmitWithSubFrameNavigation
) {
549 NavigateToFile("/password/multi_frames.html");
551 // Make sure that we prompt to save password even if a sub-frame navigation
553 NavigationObserver
observer(WebContents());
554 scoped_ptr
<PromptObserver
> prompt_observer(
555 PromptObserver::Create(WebContents()));
556 observer
.SetPathToWaitFor("/password/done.html");
557 std::string navigate_frame
=
558 "var second_iframe = document.getElementById('second_frame');"
559 "second_iframe.contentWindow.location.href = 'other.html';";
560 std::string fill_and_submit
=
561 "var first_frame = document.getElementById('first_frame');"
562 "var frame_doc = first_frame.contentDocument;"
563 "frame_doc.getElementById('username_field').value = 'temp';"
564 "frame_doc.getElementById('password_field').value = 'random';"
565 "frame_doc.getElementById('input_submit_button').click();";
567 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame
));
568 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
570 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
573 IN_PROC_BROWSER_TEST_F(
574 PasswordManagerBrowserTest
,
575 NoPromptForFailedLoginFromMainFrameWithMultiFramesInPage
) {
576 NavigateToFile("/password/multi_frames.html");
578 // Make sure that we don't prompt to save the password for a failed login
579 // from the main frame with multiple frames in the same page.
580 NavigationObserver
observer(WebContents());
581 scoped_ptr
<PromptObserver
> prompt_observer(
582 PromptObserver::Create(WebContents()));
583 std::string fill_and_submit
=
584 "document.getElementById('username_failed').value = 'temp';"
585 "document.getElementById('password_failed').value = 'random';"
586 "document.getElementById('submit_failed').click();";
588 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
590 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
593 IN_PROC_BROWSER_TEST_F(
594 PasswordManagerBrowserTest
,
595 NoPromptForFailedLoginFromSubFrameWithMultiFramesInPage
) {
596 NavigateToFile("/password/multi_frames.html");
598 // Make sure that we don't prompt to save the password for a failed login
599 // from a sub-frame with multiple frames in the same page.
600 NavigationObserver
observer(WebContents());
601 scoped_ptr
<PromptObserver
> prompt_observer(
602 PromptObserver::Create(WebContents()));
603 std::string fill_and_submit
=
604 "var first_frame = document.getElementById('first_frame');"
605 "var frame_doc = first_frame.contentDocument;"
606 "frame_doc.getElementById('username_failed').value = 'temp';"
607 "frame_doc.getElementById('password_failed').value = 'random';"
608 "frame_doc.getElementById('submit_failed').click();"
609 "window.parent.location.href = 'multi_frames.html';";
611 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
613 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
616 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, PromptForXHRSubmit
) {
617 #if defined(OS_WIN) && defined(USE_ASH)
618 // Disable this test in Metro+Ash for now (http://crbug.com/262796).
619 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests
))
622 NavigateToFile("/password/password_xhr_submit.html");
624 // Verify that we show the save password prompt if a form returns false
625 // in its onsubmit handler but instead logs in/navigates via XHR.
626 // Note that calling 'submit()' on a form with javascript doesn't call
627 // the onsubmit handler, so we click the submit button instead.
628 NavigationObserver
observer(WebContents());
629 scoped_ptr
<PromptObserver
> prompt_observer(
630 PromptObserver::Create(WebContents()));
631 std::string fill_and_submit
=
632 "document.getElementById('username_field').value = 'temp';"
633 "document.getElementById('password_field').value = 'random';"
634 "document.getElementById('submit_button').click()";
635 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
637 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
640 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
641 PromptForXHRWithoutOnSubmit
) {
642 NavigateToFile("/password/password_xhr_submit.html");
644 // Verify that if XHR navigation occurs and the form is properly filled out,
645 // we try and save the password even though onsubmit hasn't been called.
646 NavigationObserver
observer(WebContents());
647 scoped_ptr
<PromptObserver
> prompt_observer(
648 PromptObserver::Create(WebContents()));
649 std::string fill_and_navigate
=
650 "document.getElementById('username_field').value = 'temp';"
651 "document.getElementById('password_field').value = 'random';"
653 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate
));
655 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
658 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, NoPromptIfLinkClicked
) {
659 NavigateToFile("/password/password_form.html");
661 // Verify that if the user takes a direct action to leave the page, we don't
662 // prompt to save the password even if the form is already filled out.
663 NavigationObserver
observer(WebContents());
664 scoped_ptr
<PromptObserver
> prompt_observer(
665 PromptObserver::Create(WebContents()));
666 std::string fill_and_click_link
=
667 "document.getElementById('username_field').value = 'temp';"
668 "document.getElementById('password_field').value = 'random';"
669 "document.getElementById('link').click();";
670 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_click_link
));
672 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
675 // TODO(jam): http://crbug.com/350550
677 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
678 VerifyPasswordGenerationUpload
) {
679 // Prevent Autofill requests from actually going over the wire.
680 net::TestURLFetcherFactory factory
;
681 // Disable Autofill requesting access to AddressBook data. This causes
682 // the test to hang on Mac.
683 autofill::test::DisableSystemServices(browser()->profile()->GetPrefs());
685 // Visit a signup form.
686 NavigateToFile("/password/signup_form.html");
688 // Enter a password and save it.
689 NavigationObserver
first_observer(WebContents());
690 scoped_ptr
<PromptObserver
> prompt_observer(
691 PromptObserver::Create(WebContents()));
692 std::string fill_and_submit
=
693 "document.getElementById('other_info').value = 'stuff';"
694 "document.getElementById('username_field').value = 'my_username';"
695 "document.getElementById('password_field').value = 'password';"
696 "document.getElementById('input_submit_button').click()";
697 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
699 first_observer
.Wait();
700 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
701 prompt_observer
->Accept();
703 // Now navigate to a login form that has similar HTML markup.
704 NavigateToFile("/password/password_form.html");
706 // Simulate a user click to force an autofill of the form's DOM value, not
707 // just the suggested value.
708 content::SimulateMouseClick(
709 WebContents(), 0, blink::WebMouseEvent::ButtonLeft
);
711 // The form should be filled with the previously submitted username.
712 std::string get_username
=
713 "window.domAutomationController.send("
714 "document.getElementById('username_field').value);";
715 std::string actual_username
;
716 ASSERT_TRUE(content::ExecuteScriptAndExtractString(RenderViewHost(),
719 ASSERT_EQ("my_username", actual_username
);
721 // Submit the form and verify that there is no infobar (as the password
722 // has already been saved).
723 NavigationObserver
second_observer(WebContents());
724 scoped_ptr
<PromptObserver
> second_prompt_observer(
725 PromptObserver::Create(WebContents()));
726 std::string submit_form
=
727 "document.getElementById('input_submit_button').click()";
728 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit_form
));
729 second_observer
.Wait();
730 EXPECT_FALSE(second_prompt_observer
->IsShowingPrompt());
732 // Verify that we sent a ping to Autofill saying that the original form
733 // was likely an account creation form since it has more than 2 text input
734 // fields and was used for the first time on a different form.
735 base::HistogramBase
* upload_histogram
=
736 base::StatisticsRecorder::FindHistogram(
737 "PasswordGeneration.UploadStarted");
738 ASSERT_TRUE(upload_histogram
);
739 scoped_ptr
<base::HistogramSamples
> snapshot
=
740 upload_histogram
->SnapshotSamples();
741 EXPECT_EQ(0, snapshot
->GetCount(0 /* failure */));
742 EXPECT_EQ(1, snapshot
->GetCount(1 /* success */));
746 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, PromptForSubmitFromIframe
) {
747 NavigateToFile("/password/password_submit_from_iframe.html");
749 // Submit a form in an iframe, then cause the whole page to navigate without a
750 // user gesture. We expect the save password prompt to be shown here, because
751 // some pages use such iframes for login forms.
752 NavigationObserver
observer(WebContents());
753 scoped_ptr
<PromptObserver
> prompt_observer(
754 PromptObserver::Create(WebContents()));
755 std::string fill_and_submit
=
756 "var iframe = document.getElementById('test_iframe');"
757 "var iframe_doc = iframe.contentDocument;"
758 "iframe_doc.getElementById('username_field').value = 'temp';"
759 "iframe_doc.getElementById('password_field').value = 'random';"
760 "iframe_doc.getElementById('submit_button').click()";
762 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
764 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
767 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
768 PromptForInputElementWithoutName
) {
769 // Check that the prompt is shown for forms where input elements lack the
770 // "name" attribute but the "id" is present.
771 NavigateToFile("/password/password_form.html");
773 NavigationObserver
observer(WebContents());
774 scoped_ptr
<PromptObserver
> prompt_observer(
775 PromptObserver::Create(WebContents()));
776 std::string fill_and_submit
=
777 "document.getElementById('username_field_no_name').value = 'temp';"
778 "document.getElementById('password_field_no_name').value = 'random';"
779 "document.getElementById('input_submit_button_no_name').click()";
780 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
782 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
785 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
786 PromptForInputElementWithoutId
) {
787 // Check that the prompt is shown for forms where input elements lack the
788 // "id" attribute but the "name" attribute is present.
789 NavigateToFile("/password/password_form.html");
791 NavigationObserver
observer(WebContents());
792 scoped_ptr
<PromptObserver
> prompt_observer(
793 PromptObserver::Create(WebContents()));
794 std::string fill_and_submit
=
795 "document.getElementsByName('username_field_no_id')[0].value = 'temp';"
796 "document.getElementsByName('password_field_no_id')[0].value = 'random';"
797 "document.getElementsByName('input_submit_button_no_id')[0].click()";
798 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
800 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
803 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
804 NoPromptForInputElementWithoutIdAndName
) {
805 // Check that no prompt is shown for forms where the input fields lack both
806 // the "id" and the "name" attributes.
807 NavigateToFile("/password/password_form.html");
809 NavigationObserver
observer(WebContents());
810 scoped_ptr
<PromptObserver
> prompt_observer(
811 PromptObserver::Create(WebContents()));
812 std::string fill_and_submit
=
813 "var form = document.getElementById('testform_elements_no_id_no_name');"
814 "var username = form.children[0];"
815 "username.value = 'temp';"
816 "var password = form.children[1];"
817 "password.value = 'random';"
818 "form.children[2].click()"; // form.children[2] is the submit button.
819 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
821 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
824 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, DeleteFrameBeforeSubmit
) {
825 NavigateToFile("/password/multi_frames.html");
827 NavigationObserver
observer(WebContents());
828 // Make sure we save some password info from an iframe and then destroy it.
829 std::string save_and_remove
=
830 "var first_frame = document.getElementById('first_frame');"
831 "var frame_doc = first_frame.contentDocument;"
832 "frame_doc.getElementById('username_field').value = 'temp';"
833 "frame_doc.getElementById('password_field').value = 'random';"
834 "frame_doc.getElementById('input_submit_button').click();"
835 "first_frame.parentNode.removeChild(first_frame);";
836 // Submit from the main frame, but without navigating through the onsubmit
838 std::string navigate_frame
=
839 "document.getElementById('username_field').value = 'temp';"
840 "document.getElementById('password_field').value = 'random';"
841 "document.getElementById('input_submit_button').click();"
842 "window.location.href = 'done.html';";
844 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), save_and_remove
));
845 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame
));
847 // The only thing we check here is that there is no use-after-free reported.
850 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, PasswordValueAccessible
) {
851 NavigateToFile("/password/form_and_link.html");
853 // Click on a link to open a new tab, then switch back to the first one.
854 EXPECT_EQ(1, browser()->tab_strip_model()->count());
856 "document.getElementById('testlink').click();";
857 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), click
));
858 EXPECT_EQ(2, browser()->tab_strip_model()->count());
859 browser()->tab_strip_model()->ActivateTabAt(0, false);
861 // Fill in the credentials, and make sure they are saved.
862 NavigationObserver
form_submit_observer(WebContents());
863 scoped_ptr
<PromptObserver
> prompt_observer(
864 PromptObserver::Create(WebContents()));
865 std::string fill_and_submit
=
866 "document.getElementById('username_field').value = 'temp';"
867 "document.getElementById('password_field').value = 'random';"
868 "document.getElementById('input_submit_button').click();";
869 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
870 form_submit_observer
.Wait();
871 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
872 prompt_observer
->Accept();
874 // Reload the original page to have the saved credentials autofilled.
875 NavigationObserver
reload_observer(WebContents());
876 NavigateToFile("/password/form_and_link.html");
877 reload_observer
.Wait();
879 // Wait until the username is filled, to make sure autofill kicked in.
880 WaitForElementValue("username_field", "temp");
881 // Now check that the password is not accessible yet.
882 CheckElementValue("password_field", "");
883 // Let the user interact with the page.
884 content::SimulateMouseClickAt(
885 WebContents(), 0, blink::WebMouseEvent::ButtonLeft
, gfx::Point(1, 1));
886 // Wait until that interaction causes the password value to be revealed.
887 WaitForElementValue("password_field", "random");
888 // And check that after the side-effects of the interaction took place, the
889 // username value stays the same.
890 CheckElementValue("username_field", "temp");
893 // The following test is limited to Aura, because
894 // RenderWidgetHostViewGuest::ProcessAckedTouchEvent is, and
895 // ProcessAckedTouchEvent is what triggers the translation of touch events to
897 #if defined(USE_AURA)
898 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
899 PasswordValueAccessibleOnSubmit
) {
900 NavigateToFile("/password/form_and_link.html");
902 // Fill in the credentials, and make sure they are saved.
903 NavigationObserver
form_submit_observer(WebContents());
904 scoped_ptr
<PromptObserver
> prompt_observer(
905 PromptObserver::Create(WebContents()));
906 std::string fill_and_submit
=
907 "document.getElementById('username_field').value = 'temp';"
908 "document.getElementById('password_field').value = 'random_secret';"
909 "document.getElementById('input_submit_button').click();";
910 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
911 form_submit_observer
.Wait();
912 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
913 prompt_observer
->Accept();
915 // Reload the original page to have the saved credentials autofilled.
916 NavigationObserver
reload_observer(WebContents());
917 NavigateToFile("/password/form_and_link.html");
918 reload_observer
.Wait();
920 NavigationObserver
submit_observer(WebContents());
921 // Submit the form via a tap on the submit button. The button is placed at 0,
922 // 100, and has height 300 and width 700.
923 content::SimulateTapAt(WebContents(), gfx::Point(350, 250));
924 submit_observer
.Wait();
925 std::string query
= WebContents()->GetURL().query();
926 EXPECT_NE(std::string::npos
, query
.find("random_secret")) << query
;
930 // Test fix for crbug.com/338650.
931 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
932 DontPromptForPasswordFormWithDefaultValue
) {
933 NavigateToFile("/password/password_form_with_default_value.html");
935 // Don't prompt if we navigate away even if there is a password value since
936 // it's not coming from the user.
937 NavigationObserver
observer(WebContents());
938 scoped_ptr
<PromptObserver
> prompt_observer(
939 PromptObserver::Create(WebContents()));
940 NavigateToFile("/password/done.html");
942 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
945 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
946 PromptWhenEnableAutomaticPasswordSavingSwitchIsNotSet
) {
947 NavigateToFile("/password/password_form.html");
949 // Fill a form and submit through a <input type="submit"> button.
950 NavigationObserver
observer(WebContents());
951 scoped_ptr
<PromptObserver
> prompt_observer(
952 PromptObserver::Create(WebContents()));
953 std::string fill_and_submit
=
954 "document.getElementById('username_field').value = 'temp';"
955 "document.getElementById('password_field').value = 'random';"
956 "document.getElementById('input_submit_button').click()";
957 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
959 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
962 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
963 DontPromptWhenEnableAutomaticPasswordSavingSwitchIsSet
) {
964 password_manager::TestPasswordStore
* password_store
=
965 static_cast<password_manager::TestPasswordStore
*>(
966 PasswordStoreFactory::GetForProfile(browser()->profile(),
967 Profile::IMPLICIT_ACCESS
).get());
969 EXPECT_TRUE(password_store
->IsEmpty());
971 NavigateToFile("/password/password_form.html");
973 // Add the enable-automatic-password-saving switch.
974 CommandLine::ForCurrentProcess()->AppendSwitch(
975 password_manager::switches::kEnableAutomaticPasswordSaving
);
977 // Fill a form and submit through a <input type="submit"> button.
978 NavigationObserver
observer(WebContents());
979 scoped_ptr
<PromptObserver
> prompt_observer(
980 PromptObserver::Create(WebContents()));
981 // Make sure that the only passwords saved are the auto-saved ones.
982 std::string fill_and_submit
=
983 "document.getElementById('username_field').value = 'temp';"
984 "document.getElementById('password_field').value = 'random';"
985 "document.getElementById('input_submit_button').click()";
986 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
988 if (chrome::VersionInfo::GetChannel() ==
989 chrome::VersionInfo::CHANNEL_UNKNOWN
) {
990 // Passwords getting auto-saved, no prompt.
991 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
992 EXPECT_FALSE(password_store
->IsEmpty());
994 // Prompt shown, and no passwords saved automatically.
995 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
996 EXPECT_TRUE(password_store
->IsEmpty());
1000 // Test fix for crbug.com/368690.
1001 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, NoPromptWhenReloading
) {
1002 NavigateToFile("/password/password_form.html");
1005 "document.getElementById('username_redirect').value = 'temp';"
1006 "document.getElementById('password_redirect').value = 'random';";
1007 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill
));
1009 NavigationObserver
observer(WebContents());
1010 scoped_ptr
<PromptObserver
> prompt_observer(
1011 PromptObserver::Create(WebContents()));
1012 GURL url
= embedded_test_server()->GetURL("/password/password_form.html");
1013 chrome::NavigateParams
params(browser(), url
,
1014 content::PAGE_TRANSITION_RELOAD
);
1015 ui_test_utils::NavigateToURL(¶ms
);
1017 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
1020 // Test that if a form gets dynamically added between the form parsing and
1021 // rendering, and while the main frame still loads, it still is registered, and
1022 // thus saving passwords from it works.
1023 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1024 FormsAddedBetweenParsingAndRendering
) {
1025 NavigateToFile("/password/between_parsing_and_rendering.html");
1027 NavigationObserver
observer(WebContents());
1028 scoped_ptr
<PromptObserver
> prompt_observer(
1029 PromptObserver::Create(WebContents()));
1030 std::string submit
=
1031 "document.getElementById('username').value = 'temp';"
1032 "document.getElementById('password').value = 'random';"
1033 "document.getElementById('submit-button').click();";
1034 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit
));
1037 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1040 // Test that if there was no previous page load then the PasswordManagerDriver
1041 // does not think that there were SSL errors on the current page. The test opens
1042 // a new tab with a URL for which the embedded test server issues a basic auth
1044 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, NoLastLoadGoodLastLoad
) {
1045 // Teach the embedded server to handle requests by issuing the basic auth
1047 embedded_test_server()->RegisterRequestHandler(
1048 base::Bind(&HandleTestAuthRequest
));
1050 LoginPromptBrowserTestObserver login_observer
;
1051 // We need to register to all sources, because the navigation observer we are
1052 // interested in is for a new tab to be opened, and thus does not exist yet.
1053 login_observer
.Register(content::NotificationService::AllSources());
1055 password_manager::TestPasswordStore
* password_store
=
1056 static_cast<password_manager::TestPasswordStore
*>(
1057 PasswordStoreFactory::GetForProfile(browser()->profile(),
1058 Profile::IMPLICIT_ACCESS
).get());
1059 EXPECT_TRUE(password_store
->IsEmpty());
1061 // Navigate to a page requiring HTTP auth. Wait for the tab to get the correct
1062 // WebContents, but don't wait for navigation, which only finishes after
1064 ui_test_utils::NavigateToURLWithDisposition(
1066 embedded_test_server()->GetURL("/basic_auth"),
1068 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB
);
1070 content::NavigationController
* nav_controller
=
1071 &WebContents()->GetController();
1072 NavigationObserver
nav_observer(WebContents());
1073 scoped_ptr
<PromptObserver
> prompt_observer(
1074 PromptObserver::Create(WebContents()));
1075 WindowedAuthNeededObserver
auth_needed_observer(nav_controller
);
1076 auth_needed_observer
.Wait();
1078 WindowedAuthSuppliedObserver
auth_supplied_observer(nav_controller
);
1079 // Offer valid credentials on the auth challenge.
1080 ASSERT_EQ(1u, login_observer
.handlers().size());
1081 LoginHandler
* handler
= *login_observer
.handlers().begin();
1082 ASSERT_TRUE(handler
);
1083 // Any username/password will work.
1084 handler
->SetAuth(base::UTF8ToUTF16("user"), base::UTF8ToUTF16("pwd"));
1085 auth_supplied_observer
.Wait();
1087 // The password manager should be working correctly.
1088 nav_observer
.Wait();
1089 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1090 prompt_observer
->Accept();
1092 // Spin the message loop to make sure the password store had a chance to save
1094 base::RunLoop run_loop
;
1095 run_loop
.RunUntilIdle();
1096 EXPECT_FALSE(password_store
->IsEmpty());
1099 // In some situations, multiple PasswordFormManager instances from
1100 // PasswordManager::pending_login_managers_ would match (via DoesManage) a form
1101 // to be provisionally saved. One of them might be a complete match, the other
1102 // all-but-action match. Normally, the former should be preferred, but if the
1103 // former has not finished matching, and the latter has, the latter should be
1104 // used (otherwise we'd give up even though we could have saved the password).
1105 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1106 PreferPasswordFormManagerWhichFinishedMatching
) {
1107 NavigateToFile("/password/create_form_copy_on_submit.html");
1109 NavigationObserver
observer(WebContents());
1110 scoped_ptr
<PromptObserver
> prompt_observer(
1111 PromptObserver::Create(WebContents()));
1112 std::string submit
=
1113 "document.getElementById('username').value = 'overwrite_me';"
1114 "document.getElementById('password').value = 'random';"
1115 "document.getElementById('non-form-button').click();";
1116 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit
));
1119 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());