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/path_service.h"
11 #include "base/run_loop.h"
12 #include "base/stl_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/infobars/infobar_service.h"
17 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
18 #include "chrome/browser/password_manager/password_store_factory.h"
19 #include "chrome/browser/password_manager/test_password_store_service.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/login/login_prompt.h"
23 #include "chrome/browser/ui/login/login_prompt_test_utils.h"
24 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
25 #include "chrome/browser/ui/tabs/tab_strip_model.h"
26 #include "chrome/common/chrome_paths.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/chrome_version_info.h"
29 #include "chrome/test/base/in_process_browser_test.h"
30 #include "chrome/test/base/test_switches.h"
31 #include "chrome/test/base/ui_test_utils.h"
32 #include "components/autofill/core/browser/autofill_test_utils.h"
33 #include "components/autofill/core/browser/test_autofill_client.h"
34 #include "components/infobars/core/confirm_infobar_delegate.h"
35 #include "components/infobars/core/infobar.h"
36 #include "components/infobars/core/infobar_manager.h"
37 #include "components/password_manager/content/browser/content_password_manager_driver.h"
38 #include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
39 #include "components/password_manager/core/browser/test_password_store.h"
40 #include "components/password_manager/core/common/password_manager_switches.h"
41 #include "content/public/browser/navigation_controller.h"
42 #include "content/public/browser/notification_service.h"
43 #include "content/public/browser/render_frame_host.h"
44 #include "content/public/browser/render_view_host.h"
45 #include "content/public/browser/web_contents.h"
46 #include "content/public/browser/web_contents_observer.h"
47 #include "content/public/common/content_switches.h"
48 #include "content/public/test/browser_test_utils.h"
49 #include "content/public/test/test_utils.h"
50 #include "net/base/filename_util.h"
51 #include "net/test/embedded_test_server/embedded_test_server.h"
52 #include "net/test/embedded_test_server/http_request.h"
53 #include "net/test/embedded_test_server/http_response.h"
54 #include "net/test/spawned_test_server/spawned_test_server.h"
55 #include "net/url_request/test_url_fetcher_factory.h"
56 #include "testing/gmock/include/gmock/gmock.h"
57 #include "third_party/WebKit/public/web/WebInputEvent.h"
58 #include "ui/events/keycodes/keyboard_codes.h"
59 #include "ui/gfx/geometry/point.h"
62 // NavigationObserver ---------------------------------------------------------
66 // Observer that waits for navigation to complete and for the password infobar
68 class NavigationObserver
: public content::WebContentsObserver
{
70 explicit NavigationObserver(content::WebContents
* web_contents
)
71 : content::WebContentsObserver(web_contents
),
72 quit_on_entry_commited_(false),
73 message_loop_runner_(new content::MessageLoopRunner
) {}
75 ~NavigationObserver() override
{}
77 // Normally Wait() will not return until a main frame navigation occurs.
78 // If a path is set, Wait() will return after this path has been seen,
79 // regardless of the frame that navigated. Useful for multi-frame pages.
80 void SetPathToWaitFor(const std::string
& path
) {
81 wait_for_path_
= path
;
84 // Normally Wait() will not return until a main frame navigation occurs.
85 // If quit_on_entry_commited is true Wait() will return on EntryCommited.
86 void SetQuitOnEntryCommitted(bool quit_on_entry_commited
) {
87 quit_on_entry_commited_
= quit_on_entry_commited
;
90 // content::WebContentsObserver:
91 void DidFinishLoad(content::RenderFrameHost
* render_frame_host
,
92 const GURL
& validated_url
) override
{
93 if (!wait_for_path_
.empty()) {
94 if (validated_url
.path() == wait_for_path_
)
95 message_loop_runner_
->Quit();
96 } else if (!render_frame_host
->GetParent()) {
97 message_loop_runner_
->Quit();
100 void NavigationEntryCommitted(
101 const content::LoadCommittedDetails
& load_details
) override
{
102 if (quit_on_entry_commited_
)
103 message_loop_runner_
->Quit();
105 void Wait() { message_loop_runner_
->Run(); }
108 std::string wait_for_path_
;
109 bool quit_on_entry_commited_
;
110 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
112 DISALLOW_COPY_AND_ASSIGN(NavigationObserver
);
115 // Observes the save password prompt (bubble or infobar) for a specified
116 // WebContents, keeps track of whether or not it is currently shown, and allows
117 // accepting saving passwords through it.
118 class PromptObserver
{
120 virtual ~PromptObserver() {}
122 // Checks if the prompt is being currently shown.
123 virtual bool IsShowingPrompt() const = 0;
125 // Expecting that the prompt is shown, saves the password. Checks that the
126 // prompt is no longer visible afterwards.
127 void Accept() const {
128 EXPECT_TRUE(IsShowingPrompt());
132 // Chooses the right implementation of PromptObserver and creates an instance
134 static scoped_ptr
<PromptObserver
> Create(content::WebContents
* web_contents
);
139 // Accepts the password. The implementation can assume that the prompt is
140 // currently shown, but is required to verify that the prompt is eventually
142 virtual void AcceptImpl() const = 0;
145 DISALLOW_COPY_AND_ASSIGN(PromptObserver
);
148 class InfoBarObserver
: public PromptObserver
,
149 public infobars::InfoBarManager::Observer
{
151 explicit InfoBarObserver(content::WebContents
* web_contents
)
152 : infobar_is_being_shown_(false),
153 infobar_service_(InfoBarService::FromWebContents(web_contents
)) {
154 infobar_service_
->AddObserver(this);
157 ~InfoBarObserver() override
{
158 if (infobar_service_
)
159 infobar_service_
->RemoveObserver(this);
164 bool IsShowingPrompt() const override
{ return infobar_is_being_shown_
; }
166 void AcceptImpl() const override
{
167 EXPECT_EQ(1u, infobar_service_
->infobar_count());
168 if (!infobar_service_
->infobar_count())
169 return; // Let the test finish to gather possibly more diagnostics.
171 // ConfirmInfoBarDelegate::Accept returning true means the infobar is
172 // immediately closed. Checking the return value is preferred to testing
173 // IsShowingPrompt() here, for it avoids the delay until the closing
174 // notification is received.
175 EXPECT_TRUE(infobar_service_
->infobar_at(0)
177 ->AsConfirmInfoBarDelegate()
181 // infobars::InfoBarManager::Observer:
182 void OnInfoBarAdded(infobars::InfoBar
* infobar
) override
{
183 infobar_is_being_shown_
= true;
186 void OnInfoBarRemoved(infobars::InfoBar
* infobar
, bool animate
) override
{
187 infobar_is_being_shown_
= false;
190 void OnManagerShuttingDown(infobars::InfoBarManager
* manager
) override
{
191 ASSERT_EQ(infobar_service_
, manager
);
192 infobar_service_
->RemoveObserver(this);
193 infobar_service_
= nullptr;
196 bool infobar_is_being_shown_
;
197 InfoBarService
* infobar_service_
;
199 DISALLOW_COPY_AND_ASSIGN(InfoBarObserver
);
202 class BubbleObserver
: public PromptObserver
{
204 explicit BubbleObserver(content::WebContents
* web_contents
)
206 ManagePasswordsUIController::FromWebContents(web_contents
)) {}
208 ~BubbleObserver() override
{}
212 bool IsShowingPrompt() const override
{
213 return ui_controller_
->PasswordPendingUserDecision();
216 void AcceptImpl() const override
{
217 ui_controller_
->SavePassword();
218 EXPECT_FALSE(IsShowingPrompt());
221 ManagePasswordsUIController
* const ui_controller_
;
223 DISALLOW_COPY_AND_ASSIGN(BubbleObserver
);
226 class ObservingAutofillClient
: public autofill::TestAutofillClient
{
228 ObservingAutofillClient()
229 : message_loop_runner_(new content::MessageLoopRunner
){}
230 ~ObservingAutofillClient() override
{}
233 message_loop_runner_
->Run();
236 void ShowAutofillPopup(
237 const gfx::RectF
& element_bounds
,
238 base::i18n::TextDirection text_direction
,
239 const std::vector
<autofill::Suggestion
>& suggestions
,
240 base::WeakPtr
<autofill::AutofillPopupDelegate
> delegate
) override
{
241 message_loop_runner_
->Quit();
245 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
247 DISALLOW_COPY_AND_ASSIGN(ObservingAutofillClient
);
250 GURL
GetFileURL(const char* filename
) {
252 PathService::Get(chrome::DIR_TEST_DATA
, &path
);
253 path
= path
.AppendASCII("password").AppendASCII(filename
);
254 CHECK(base::PathExists(path
));
255 return net::FilePathToFileURL(path
);
259 scoped_ptr
<PromptObserver
> PromptObserver::Create(
260 content::WebContents
* web_contents
) {
261 if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
262 return scoped_ptr
<PromptObserver
>(new BubbleObserver(web_contents
));
264 return scoped_ptr
<PromptObserver
>(new InfoBarObserver(web_contents
));
268 // Handles |request| to "/basic_auth". If "Authorization" header is present,
269 // responds with a non-empty HTTP 200 page (regardless of its value). Otherwise
270 // serves a Basic Auth challenge.
271 scoped_ptr
<net::test_server::HttpResponse
> HandleTestAuthRequest(
272 const net::test_server::HttpRequest
& request
) {
273 if (!StartsWithASCII(request
.relative_url
, "/basic_auth", true))
274 return scoped_ptr
<net::test_server::HttpResponse
>();
276 if (ContainsKey(request
.headers
, "Authorization")) {
277 scoped_ptr
<net::test_server::BasicHttpResponse
> http_response(
278 new net::test_server::BasicHttpResponse
);
279 http_response
->set_code(net::HTTP_OK
);
280 http_response
->set_content("Success!");
281 return http_response
.Pass();
283 scoped_ptr
<net::test_server::BasicHttpResponse
> http_response(
284 new net::test_server::BasicHttpResponse
);
285 http_response
->set_code(net::HTTP_UNAUTHORIZED
);
286 http_response
->AddCustomHeader("WWW-Authenticate",
287 "Basic realm=\"test realm\"");
288 return http_response
.Pass();
295 // PasswordManagerBrowserTest -------------------------------------------------
297 class PasswordManagerBrowserTest
: public InProcessBrowserTest
{
299 PasswordManagerBrowserTest() {}
300 ~PasswordManagerBrowserTest() override
{}
302 // InProcessBrowserTest:
303 void SetUpOnMainThread() override
{
304 // Use TestPasswordStore to remove a possible race. Normally the
305 // PasswordStore does its database manipulation on the DB thread, which
306 // creates a possible race during navigation. Specifically the
307 // PasswordManager will ignore any forms in a page if the load from the
308 // PasswordStore has not completed.
309 PasswordStoreFactory::GetInstance()->SetTestingFactory(
310 browser()->profile(), TestPasswordStoreService::Build
);
311 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
312 ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
313 password_manager::switches::kEnableAutomaticPasswordSaving
));
316 void TearDownOnMainThread() override
{
317 ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
321 content::WebContents
* WebContents() {
322 return browser()->tab_strip_model()->GetActiveWebContents();
325 content::RenderViewHost
* RenderViewHost() {
326 return WebContents()->GetRenderViewHost();
329 // Wrapper around ui_test_utils::NavigateToURL that waits until
330 // DidFinishLoad() fires. Normally this function returns after
331 // DidStopLoading(), which caused flakiness as the NavigationObserver
332 // would sometimes see the DidFinishLoad event from a previous navigation and
333 // return immediately.
334 void NavigateToFile(const std::string
& path
) {
335 NavigationObserver
observer(WebContents());
336 GURL url
= embedded_test_server()->GetURL(path
);
337 ui_test_utils::NavigateToURL(browser(), url
);
341 // Waits until the "value" attribute of the HTML element with |element_id| is
342 // equal to |expected_value|. If the current value is not as expected, this
343 // waits until the "change" event is fired for the element. This also
344 // guarantees that once the real value matches the expected, the JavaScript
345 // event loop is spun to allow all other possible events to take place.
346 void WaitForElementValue(const std::string
& element_id
,
347 const std::string
& expected_value
);
348 // Checks that the current "value" attribute of the HTML element with
349 // |element_id| is equal to |expected_value|.
350 void CheckElementValue(const std::string
& element_id
,
351 const std::string
& expected_value
);
353 // TODO(dvadym): Remove this once history.pushState() handling is not behind
355 // Calling this will activate handling of pushState()-initiated form submits.
356 // This feature is currently behind a renderer flag. Just setting the flag in
357 // the browser will not change it for the already running renderer at tab 0.
358 // Therefore this method first sets the flag and then opens a new tab at
359 // position 0. This new tab gets the flag copied from the browser process.
360 void ActivateHistoryPushState();
363 DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest
);
366 void PasswordManagerBrowserTest::WaitForElementValue(
367 const std::string
& element_id
,
368 const std::string
& expected_value
) {
369 enum ReturnCodes
{ // Possible results of the JavaScript code.
371 RETURN_CODE_NO_ELEMENT
,
372 RETURN_CODE_WRONG_VALUE
,
375 const std::string value_check_function
= base::StringPrintf(
376 "function valueCheck() {"
377 " var element = document.getElementById('%s');"
378 " return element && element.value == '%s';"
381 expected_value
.c_str());
382 const std::string script
=
383 value_check_function
+
385 "if (valueCheck()) {"
386 " /* Spin the event loop with setTimeout. */"
387 " setTimeout(window.domAutomationController.send(%d), 0);"
389 " var element = document.getElementById('%s');"
391 " window.domAutomationController.send(%d);"
392 " element.onchange = function() {"
393 " if (valueCheck()) {"
394 " /* Spin the event loop with setTimeout. */"
395 " setTimeout(window.domAutomationController.send(%d), 0);"
397 " window.domAutomationController.send(%d);"
403 RETURN_CODE_NO_ELEMENT
,
405 RETURN_CODE_WRONG_VALUE
);
406 int return_value
= RETURN_CODE_INVALID
;
407 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
408 RenderViewHost(), script
, &return_value
));
409 EXPECT_EQ(RETURN_CODE_OK
, return_value
)
410 << "element_id = " << element_id
411 << ", expected_value = " << expected_value
;
414 void PasswordManagerBrowserTest::CheckElementValue(
415 const std::string
& element_id
,
416 const std::string
& expected_value
) {
417 const std::string value_check_script
= base::StringPrintf(
418 "var element = document.getElementById('%s');"
419 "window.domAutomationController.send(element && element.value == '%s');",
421 expected_value
.c_str());
422 bool return_value
= false;
423 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
424 RenderViewHost(), value_check_script
, &return_value
));
425 EXPECT_TRUE(return_value
) << "element_id = " << element_id
426 << ", expected_value = " << expected_value
;
429 void PasswordManagerBrowserTest::ActivateHistoryPushState() {
430 base::CommandLine::ForCurrentProcess()->AppendSwitch(
431 autofill::switches::kEnablePasswordSaveOnInPageNavigation
);
432 AddTabAtIndex(0, GURL(url::kAboutBlankURL
), ui::PAGE_TRANSITION_TYPED
);
435 // Actual tests ---------------------------------------------------------------
436 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
437 PromptForNormalSubmit
) {
438 NavigateToFile("/password/password_form.html");
440 // Fill a form and submit through a <input type="submit"> button. Nothing
442 NavigationObserver
observer(WebContents());
443 scoped_ptr
<PromptObserver
> prompt_observer(
444 PromptObserver::Create(WebContents()));
445 std::string fill_and_submit
=
446 "document.getElementById('username_field').value = 'temp';"
447 "document.getElementById('password_field').value = 'random';"
448 "document.getElementById('input_submit_button').click()";
449 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
451 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
454 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
455 PromptForSubmitWithInPageNavigation
) {
456 NavigateToFile("/password/password_navigate_before_submit.html");
458 // Fill a form and submit through a <input type="submit"> button. Nothing
459 // special. The form does an in-page navigation before submitting.
460 NavigationObserver
observer(WebContents());
461 scoped_ptr
<PromptObserver
> prompt_observer(
462 PromptObserver::Create(WebContents()));
463 std::string fill_and_submit
=
464 "document.getElementById('username_field').value = 'temp';"
465 "document.getElementById('password_field').value = 'random';"
466 "document.getElementById('input_submit_button').click()";
467 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
469 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
472 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
473 LoginSuccessWithUnrelatedForm
) {
474 // Log in, see a form on the landing page. That form is not related to the
475 // login form (=has a different action), so we should offer saving the
477 NavigateToFile("/password/password_form.html");
479 NavigationObserver
observer(WebContents());
480 scoped_ptr
<PromptObserver
> prompt_observer(
481 PromptObserver::Create(WebContents()));
482 std::string fill_and_submit
=
483 "document.getElementById('username_unrelated').value = 'temp';"
484 "document.getElementById('password_unrelated').value = 'random';"
485 "document.getElementById('submit_unrelated').click()";
486 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
488 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
491 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, LoginFailed
) {
492 // Log in, see a form on the landing page. That form is not related to the
493 // login form (=has a different action), so we should offer saving the
495 NavigateToFile("/password/password_form.html");
497 NavigationObserver
observer(WebContents());
498 scoped_ptr
<PromptObserver
> prompt_observer(
499 PromptObserver::Create(WebContents()));
500 std::string fill_and_submit
=
501 "document.getElementById('username_failed').value = 'temp';"
502 "document.getElementById('password_failed').value = 'random';"
503 "document.getElementById('submit_failed').click()";
504 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
506 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
509 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, Redirects
) {
510 NavigateToFile("/password/password_form.html");
512 // Fill a form and submit through a <input type="submit"> button. The form
513 // points to a redirection page.
514 NavigationObserver
observer(WebContents());
515 scoped_ptr
<PromptObserver
> prompt_observer(
516 PromptObserver::Create(WebContents()));
517 std::string fill_and_submit
=
518 "document.getElementById('username_redirect').value = 'temp';"
519 "document.getElementById('password_redirect').value = 'random';"
520 "document.getElementById('submit_redirect').click()";
521 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
523 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
525 // The redirection page now redirects via Javascript. We check that the
527 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
528 "window.location.href = 'done.html';"));
530 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
533 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
534 PromptForSubmitUsingJavaScript
) {
535 NavigateToFile("/password/password_form.html");
537 // Fill a form and submit using <button> that calls submit() on the form.
538 // This should work regardless of the type of element, as long as submit() is
540 NavigationObserver
observer(WebContents());
541 scoped_ptr
<PromptObserver
> prompt_observer(
542 PromptObserver::Create(WebContents()));
543 std::string fill_and_submit
=
544 "document.getElementById('username_field').value = 'temp';"
545 "document.getElementById('password_field').value = 'random';"
546 "document.getElementById('submit_button').click()";
547 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
549 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
552 // Flaky: crbug.com/301547, observed on win and mac. Probably happens on all
554 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
555 DISABLED_PromptForDynamicForm
) {
556 NavigateToFile("/password/dynamic_password_form.html");
558 // Fill the dynamic password form and submit.
559 NavigationObserver
observer(WebContents());
560 scoped_ptr
<PromptObserver
> prompt_observer(
561 PromptObserver::Create(WebContents()));
562 std::string fill_and_submit
=
563 "document.getElementById('create_form_button').click();"
564 "window.setTimeout(function() {"
565 " document.dynamic_form.username.value = 'tempro';"
566 " document.dynamic_form.password.value = 'random';"
567 " document.dynamic_form.submit();"
569 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
571 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
574 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, NoPromptForNavigation
) {
575 NavigateToFile("/password/password_form.html");
577 // Don't fill the password form, just navigate away. Shouldn't prompt.
578 NavigationObserver
observer(WebContents());
579 scoped_ptr
<PromptObserver
> prompt_observer(
580 PromptObserver::Create(WebContents()));
581 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
582 "window.location.href = 'done.html';"));
584 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
587 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
588 NoPromptForSubFrameNavigation
) {
589 NavigateToFile("/password/multi_frames.html");
591 // If you are filling out a password form in one frame and a different frame
592 // navigates, this should not trigger the infobar.
593 NavigationObserver
observer(WebContents());
594 scoped_ptr
<PromptObserver
> prompt_observer(
595 PromptObserver::Create(WebContents()));
596 observer
.SetPathToWaitFor("/password/done.html");
598 "var first_frame = document.getElementById('first_frame');"
599 "var frame_doc = first_frame.contentDocument;"
600 "frame_doc.getElementById('username_field').value = 'temp';"
601 "frame_doc.getElementById('password_field').value = 'random';";
602 std::string navigate_frame
=
603 "var second_iframe = document.getElementById('second_frame');"
604 "second_iframe.contentWindow.location.href = 'done.html';";
606 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill
));
607 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame
));
609 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
612 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
613 PromptAfterSubmitWithSubFrameNavigation
) {
614 NavigateToFile("/password/multi_frames.html");
616 // Make sure that we prompt to save password even if a sub-frame navigation
618 NavigationObserver
observer(WebContents());
619 scoped_ptr
<PromptObserver
> prompt_observer(
620 PromptObserver::Create(WebContents()));
621 observer
.SetPathToWaitFor("/password/done.html");
622 std::string navigate_frame
=
623 "var second_iframe = document.getElementById('second_frame');"
624 "second_iframe.contentWindow.location.href = 'other.html';";
625 std::string fill_and_submit
=
626 "var first_frame = document.getElementById('first_frame');"
627 "var frame_doc = first_frame.contentDocument;"
628 "frame_doc.getElementById('username_field').value = 'temp';"
629 "frame_doc.getElementById('password_field').value = 'random';"
630 "frame_doc.getElementById('input_submit_button').click();";
632 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame
));
633 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
635 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
638 IN_PROC_BROWSER_TEST_F(
639 PasswordManagerBrowserTest
,
640 NoPromptForFailedLoginFromMainFrameWithMultiFramesInPage
) {
641 NavigateToFile("/password/multi_frames.html");
643 // Make sure that we don't prompt to save the password for a failed login
644 // from the main frame with multiple frames in the same page.
645 NavigationObserver
observer(WebContents());
646 scoped_ptr
<PromptObserver
> prompt_observer(
647 PromptObserver::Create(WebContents()));
648 std::string fill_and_submit
=
649 "document.getElementById('username_failed').value = 'temp';"
650 "document.getElementById('password_failed').value = 'random';"
651 "document.getElementById('submit_failed').click();";
653 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
655 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
658 IN_PROC_BROWSER_TEST_F(
659 PasswordManagerBrowserTest
,
660 NoPromptForFailedLoginFromSubFrameWithMultiFramesInPage
) {
661 NavigateToFile("/password/multi_frames.html");
663 // Make sure that we don't prompt to save the password for a failed login
664 // from a sub-frame with multiple frames in the same page.
665 NavigationObserver
observer(WebContents());
666 scoped_ptr
<PromptObserver
> prompt_observer(
667 PromptObserver::Create(WebContents()));
668 std::string fill_and_submit
=
669 "var first_frame = document.getElementById('first_frame');"
670 "var frame_doc = first_frame.contentDocument;"
671 "frame_doc.getElementById('username_failed').value = 'temp';"
672 "frame_doc.getElementById('password_failed').value = 'random';"
673 "frame_doc.getElementById('submit_failed').click();"
674 "window.parent.location.href = 'multi_frames.html';";
676 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
678 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
681 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, PromptForXHRSubmit
) {
682 #if defined(OS_WIN) && defined(USE_ASH)
683 // Disable this test in Metro+Ash for now (http://crbug.com/262796).
684 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
685 switches::kAshBrowserTests
))
688 NavigateToFile("/password/password_xhr_submit.html");
690 // Verify that we show the save password prompt if a form returns false
691 // in its onsubmit handler but instead logs in/navigates via XHR.
692 // Note that calling 'submit()' on a form with javascript doesn't call
693 // the onsubmit handler, so we click the submit button instead.
694 NavigationObserver
observer(WebContents());
695 scoped_ptr
<PromptObserver
> prompt_observer(
696 PromptObserver::Create(WebContents()));
697 std::string fill_and_submit
=
698 "document.getElementById('username_field').value = 'temp';"
699 "document.getElementById('password_field').value = 'random';"
700 "document.getElementById('submit_button').click()";
701 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
703 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
706 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
707 PromptForXHRWithoutOnSubmit
) {
708 NavigateToFile("/password/password_xhr_submit.html");
710 // Verify that if XHR navigation occurs and the form is properly filled out,
711 // we try and save the password even though onsubmit hasn't been called.
712 NavigationObserver
observer(WebContents());
713 scoped_ptr
<PromptObserver
> prompt_observer(
714 PromptObserver::Create(WebContents()));
715 std::string fill_and_navigate
=
716 "document.getElementById('username_field').value = 'temp';"
717 "document.getElementById('password_field').value = 'random';"
719 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate
));
721 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
724 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
725 PromptForXHRWithNewPasswordsWithoutOnSubmit
) {
726 NavigateToFile("/password/password_xhr_submit.html");
728 // Verify that if XHR navigation occurs and the form is properly filled out,
729 // we try and save the password even though onsubmit hasn't been called.
730 // Specifically verify that the password form saving new passwords is treated
731 // the same as a login form.
732 NavigationObserver
observer(WebContents());
733 scoped_ptr
<PromptObserver
> prompt_observer(
734 PromptObserver::Create(WebContents()));
735 std::string fill_and_navigate
=
736 "document.getElementById('signup_username_field').value = 'temp';"
737 "document.getElementById('signup_password_field').value = 'random';"
738 "document.getElementById('confirmation_password_field').value = 'random';"
740 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate
));
742 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
745 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, NoPromptIfLinkClicked
) {
746 NavigateToFile("/password/password_form.html");
748 // Verify that if the user takes a direct action to leave the page, we don't
749 // prompt to save the password even if the form is already filled out.
750 NavigationObserver
observer(WebContents());
751 scoped_ptr
<PromptObserver
> prompt_observer(
752 PromptObserver::Create(WebContents()));
753 std::string fill_and_click_link
=
754 "document.getElementById('username_field').value = 'temp';"
755 "document.getElementById('password_field').value = 'random';"
756 "document.getElementById('link').click();";
757 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_click_link
));
759 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
762 // TODO(jam): http://crbug.com/350550
764 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
765 VerifyPasswordGenerationUpload
) {
766 // Prevent Autofill requests from actually going over the wire.
767 net::TestURLFetcherFactory factory
;
768 // Disable Autofill requesting access to AddressBook data. This causes
769 // the test to hang on Mac.
770 autofill::test::DisableSystemServices(browser()->profile()->GetPrefs());
772 // Visit a signup form.
773 NavigateToFile("/password/signup_form.html");
775 // Enter a password and save it.
776 NavigationObserver
first_observer(WebContents());
777 scoped_ptr
<PromptObserver
> prompt_observer(
778 PromptObserver::Create(WebContents()));
779 std::string fill_and_submit
=
780 "document.getElementById('other_info').value = 'stuff';"
781 "document.getElementById('username_field').value = 'my_username';"
782 "document.getElementById('password_field').value = 'password';"
783 "document.getElementById('input_submit_button').click()";
784 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
786 first_observer
.Wait();
787 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
788 prompt_observer
->Accept();
790 // Now navigate to a login form that has similar HTML markup.
791 NavigateToFile("/password/password_form.html");
793 // Simulate a user click to force an autofill of the form's DOM value, not
794 // just the suggested value.
795 content::SimulateMouseClick(
796 WebContents(), 0, blink::WebMouseEvent::ButtonLeft
);
798 // The form should be filled with the previously submitted username.
799 std::string get_username
=
800 "window.domAutomationController.send("
801 "document.getElementById('username_field').value);";
802 std::string actual_username
;
803 ASSERT_TRUE(content::ExecuteScriptAndExtractString(RenderViewHost(),
806 ASSERT_EQ("my_username", actual_username
);
808 // Submit the form and verify that there is no infobar (as the password
809 // has already been saved).
810 NavigationObserver
second_observer(WebContents());
811 scoped_ptr
<PromptObserver
> second_prompt_observer(
812 PromptObserver::Create(WebContents()));
813 std::string submit_form
=
814 "document.getElementById('input_submit_button').click()";
815 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit_form
));
816 second_observer
.Wait();
817 EXPECT_FALSE(second_prompt_observer
->IsShowingPrompt());
819 // Verify that we sent two pings to Autofill. One vote for of PASSWORD for
820 // the current form, and one vote for ACCOUNT_CREATION_PASSWORD on the
821 // original form since it has more than 2 text input fields and was used for
822 // the first time on a different form.
823 base::HistogramBase
* upload_histogram
=
824 base::StatisticsRecorder::FindHistogram(
825 "PasswordGeneration.UploadStarted");
826 ASSERT_TRUE(upload_histogram
);
827 scoped_ptr
<base::HistogramSamples
> snapshot
=
828 upload_histogram
->SnapshotSamples();
829 EXPECT_EQ(0, snapshot
->GetCount(0 /* failure */));
830 EXPECT_EQ(2, snapshot
->GetCount(1 /* success */));
834 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, PromptForSubmitFromIframe
) {
835 NavigateToFile("/password/password_submit_from_iframe.html");
837 // Submit a form in an iframe, then cause the whole page to navigate without a
838 // user gesture. We expect the save password prompt to be shown here, because
839 // some pages use such iframes for login forms.
840 NavigationObserver
observer(WebContents());
841 scoped_ptr
<PromptObserver
> prompt_observer(
842 PromptObserver::Create(WebContents()));
843 std::string fill_and_submit
=
844 "var iframe = document.getElementById('test_iframe');"
845 "var iframe_doc = iframe.contentDocument;"
846 "iframe_doc.getElementById('username_field').value = 'temp';"
847 "iframe_doc.getElementById('password_field').value = 'random';"
848 "iframe_doc.getElementById('submit_button').click()";
850 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
852 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
855 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
856 PromptForInputElementWithoutName
) {
857 // Check that the prompt is shown for forms where input elements lack the
858 // "name" attribute but the "id" is present.
859 NavigateToFile("/password/password_form.html");
861 NavigationObserver
observer(WebContents());
862 scoped_ptr
<PromptObserver
> prompt_observer(
863 PromptObserver::Create(WebContents()));
864 std::string fill_and_submit
=
865 "document.getElementById('username_field_no_name').value = 'temp';"
866 "document.getElementById('password_field_no_name').value = 'random';"
867 "document.getElementById('input_submit_button_no_name').click()";
868 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
870 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
873 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
874 PromptForInputElementWithoutId
) {
875 // Check that the prompt is shown for forms where input elements lack the
876 // "id" attribute but the "name" attribute is present.
877 NavigateToFile("/password/password_form.html");
879 NavigationObserver
observer(WebContents());
880 scoped_ptr
<PromptObserver
> prompt_observer(
881 PromptObserver::Create(WebContents()));
882 std::string fill_and_submit
=
883 "document.getElementsByName('username_field_no_id')[0].value = 'temp';"
884 "document.getElementsByName('password_field_no_id')[0].value = 'random';"
885 "document.getElementsByName('input_submit_button_no_id')[0].click()";
886 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
888 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
891 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
892 NoPromptForInputElementWithoutIdAndName
) {
893 // Check that no prompt is shown for forms where the input fields lack both
894 // the "id" and the "name" attributes.
895 NavigateToFile("/password/password_form.html");
897 NavigationObserver
observer(WebContents());
898 scoped_ptr
<PromptObserver
> prompt_observer(
899 PromptObserver::Create(WebContents()));
900 std::string fill_and_submit
=
901 "var form = document.getElementById('testform_elements_no_id_no_name');"
902 "var username = form.children[0];"
903 "username.value = 'temp';"
904 "var password = form.children[1];"
905 "password.value = 'random';"
906 "form.children[2].click()"; // form.children[2] is the submit button.
907 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
909 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
912 // Test for checking that no prompt is shown for URLs with file: scheme.
913 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
914 NoPromptForFileSchemeURLs
) {
915 GURL url
= GetFileURL("password_form.html");
916 ui_test_utils::NavigateToURL(browser(), url
);
918 NavigationObserver
observer(WebContents());
919 scoped_ptr
<PromptObserver
> prompt_observer(
920 PromptObserver::Create(WebContents()));
921 std::string fill_and_submit
=
922 "document.getElementById('username_field').value = 'temp';"
923 "document.getElementById('password_field').value = 'random';"
924 "document.getElementById('input_submit_button').click();";
925 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
927 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
930 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
931 NoPromptForLandingPageWithHTTPErrorStatusCode
) {
932 // Check that no prompt is shown for forms where the landing page has
934 NavigateToFile("/password/password_form.html");
936 NavigationObserver
observer(WebContents());
937 scoped_ptr
<PromptObserver
> prompt_observer(
938 PromptObserver::Create(WebContents()));
939 std::string fill_and_submit
=
940 "document.getElementById('username_field_http_error').value = 'temp';"
941 "document.getElementById('password_field_http_error').value = 'random';"
942 "document.getElementById('input_submit_button_http_error').click()";
943 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
945 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
948 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, DeleteFrameBeforeSubmit
) {
949 NavigateToFile("/password/multi_frames.html");
951 NavigationObserver
observer(WebContents());
952 // Make sure we save some password info from an iframe and then destroy it.
953 std::string save_and_remove
=
954 "var first_frame = document.getElementById('first_frame');"
955 "var frame_doc = first_frame.contentDocument;"
956 "frame_doc.getElementById('username_field').value = 'temp';"
957 "frame_doc.getElementById('password_field').value = 'random';"
958 "frame_doc.getElementById('input_submit_button').click();"
959 "first_frame.parentNode.removeChild(first_frame);";
960 // Submit from the main frame, but without navigating through the onsubmit
962 std::string navigate_frame
=
963 "document.getElementById('username_field').value = 'temp';"
964 "document.getElementById('password_field').value = 'random';"
965 "document.getElementById('input_submit_button').click();"
966 "window.location.href = 'done.html';";
968 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), save_and_remove
));
969 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame
));
971 // The only thing we check here is that there is no use-after-free reported.
974 // Disabled on Windows due to flakiness: http://crbug.com/346297
976 #define MAYBE_PasswordValueAccessible DISABLED_PasswordValueAccessible
978 #define MAYBE_PasswordValueAccessible PasswordValueAccessible
980 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
981 MAYBE_PasswordValueAccessible
) {
982 NavigateToFile("/password/form_and_link.html");
984 // Click on a link to open a new tab, then switch back to the first one.
985 EXPECT_EQ(1, browser()->tab_strip_model()->count());
987 "document.getElementById('testlink').click();";
988 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), click
));
989 EXPECT_EQ(2, browser()->tab_strip_model()->count());
990 browser()->tab_strip_model()->ActivateTabAt(0, false);
992 // Fill in the credentials, and make sure they are saved.
993 NavigationObserver
form_submit_observer(WebContents());
994 scoped_ptr
<PromptObserver
> prompt_observer(
995 PromptObserver::Create(WebContents()));
996 std::string fill_and_submit
=
997 "document.getElementById('username_field').value = 'temp';"
998 "document.getElementById('password_field').value = 'random';"
999 "document.getElementById('input_submit_button').click();";
1000 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
1001 form_submit_observer
.Wait();
1002 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1003 prompt_observer
->Accept();
1005 // Reload the original page to have the saved credentials autofilled.
1006 NavigationObserver
reload_observer(WebContents());
1007 NavigateToFile("/password/form_and_link.html");
1008 reload_observer
.Wait();
1010 // Wait until the username is filled, to make sure autofill kicked in.
1011 WaitForElementValue("username_field", "temp");
1012 // Now check that the password is not accessible yet.
1013 CheckElementValue("password_field", "");
1014 // Let the user interact with the page.
1015 content::SimulateMouseClickAt(
1016 WebContents(), 0, blink::WebMouseEvent::ButtonLeft
, gfx::Point(1, 1));
1017 // Wait until that interaction causes the password value to be revealed.
1018 WaitForElementValue("password_field", "random");
1019 // And check that after the side-effects of the interaction took place, the
1020 // username value stays the same.
1021 CheckElementValue("username_field", "temp");
1024 // The following test is limited to Aura, because
1025 // RenderWidgetHostViewGuest::ProcessAckedTouchEvent is, and
1026 // ProcessAckedTouchEvent is what triggers the translation of touch events to
1028 // Disabled: http://crbug.com/346297
1029 #if defined(USE_AURA)
1030 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1031 DISABLED_PasswordValueAccessibleOnSubmit
) {
1032 NavigateToFile("/password/form_and_link.html");
1034 // Fill in the credentials, and make sure they are saved.
1035 NavigationObserver
form_submit_observer(WebContents());
1036 scoped_ptr
<PromptObserver
> prompt_observer(
1037 PromptObserver::Create(WebContents()));
1038 std::string fill_and_submit
=
1039 "document.getElementById('username_field').value = 'temp';"
1040 "document.getElementById('password_field').value = 'random_secret';"
1041 "document.getElementById('input_submit_button').click();";
1042 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
1043 form_submit_observer
.Wait();
1044 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1045 prompt_observer
->Accept();
1047 // Reload the original page to have the saved credentials autofilled.
1048 NavigationObserver
reload_observer(WebContents());
1049 NavigateToFile("/password/form_and_link.html");
1050 reload_observer
.Wait();
1052 NavigationObserver
submit_observer(WebContents());
1053 // Submit the form via a tap on the submit button. The button is placed at 0,
1054 // 100, and has height 300 and width 700.
1055 content::SimulateTapAt(WebContents(), gfx::Point(350, 250));
1056 submit_observer
.Wait();
1057 std::string query
= WebContents()->GetURL().query();
1058 EXPECT_NE(std::string::npos
, query
.find("random_secret")) << query
;
1062 // Test fix for crbug.com/338650.
1063 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1064 DontPromptForPasswordFormWithDefaultValue
) {
1065 NavigateToFile("/password/password_form_with_default_value.html");
1067 // Don't prompt if we navigate away even if there is a password value since
1068 // it's not coming from the user.
1069 NavigationObserver
observer(WebContents());
1070 scoped_ptr
<PromptObserver
> prompt_observer(
1071 PromptObserver::Create(WebContents()));
1072 NavigateToFile("/password/done.html");
1074 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
1077 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1078 PromptWhenEnableAutomaticPasswordSavingSwitchIsNotSet
) {
1079 NavigateToFile("/password/password_form.html");
1081 // Fill a form and submit through a <input type="submit"> button.
1082 NavigationObserver
observer(WebContents());
1083 scoped_ptr
<PromptObserver
> prompt_observer(
1084 PromptObserver::Create(WebContents()));
1085 std::string fill_and_submit
=
1086 "document.getElementById('username_field').value = 'temp';"
1087 "document.getElementById('password_field').value = 'random';"
1088 "document.getElementById('input_submit_button').click()";
1089 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
1091 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1094 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1095 DontPromptWhenEnableAutomaticPasswordSavingSwitchIsSet
) {
1096 password_manager::TestPasswordStore
* password_store
=
1097 static_cast<password_manager::TestPasswordStore
*>(
1098 PasswordStoreFactory::GetForProfile(
1099 browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS
).get());
1101 EXPECT_TRUE(password_store
->IsEmpty());
1103 NavigateToFile("/password/password_form.html");
1105 // Add the enable-automatic-password-saving switch.
1106 base::CommandLine::ForCurrentProcess()->AppendSwitch(
1107 password_manager::switches::kEnableAutomaticPasswordSaving
);
1109 // Fill a form and submit through a <input type="submit"> button.
1110 NavigationObserver
observer(WebContents());
1111 scoped_ptr
<PromptObserver
> prompt_observer(
1112 PromptObserver::Create(WebContents()));
1113 // Make sure that the only passwords saved are the auto-saved ones.
1114 std::string fill_and_submit
=
1115 "document.getElementById('username_field').value = 'temp';"
1116 "document.getElementById('password_field').value = 'random';"
1117 "document.getElementById('input_submit_button').click()";
1118 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
1120 if (chrome::VersionInfo::GetChannel() ==
1121 chrome::VersionInfo::CHANNEL_UNKNOWN
) {
1122 // Passwords getting auto-saved, no prompt.
1123 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
1124 EXPECT_FALSE(password_store
->IsEmpty());
1126 // Prompt shown, and no passwords saved automatically.
1127 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1128 EXPECT_TRUE(password_store
->IsEmpty());
1132 // Test fix for crbug.com/368690.
1133 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, NoPromptWhenReloading
) {
1134 NavigateToFile("/password/password_form.html");
1137 "document.getElementById('username_redirect').value = 'temp';"
1138 "document.getElementById('password_redirect').value = 'random';";
1139 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill
));
1141 NavigationObserver
observer(WebContents());
1142 scoped_ptr
<PromptObserver
> prompt_observer(
1143 PromptObserver::Create(WebContents()));
1144 GURL url
= embedded_test_server()->GetURL("/password/password_form.html");
1145 chrome::NavigateParams
params(browser(), url
,
1146 ui::PAGE_TRANSITION_RELOAD
);
1147 ui_test_utils::NavigateToURL(¶ms
);
1149 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
1152 // Test that if a form gets dynamically added between the form parsing and
1153 // rendering, and while the main frame still loads, it still is registered, and
1154 // thus saving passwords from it works.
1155 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1156 FormsAddedBetweenParsingAndRendering
) {
1157 NavigateToFile("/password/between_parsing_and_rendering.html");
1159 NavigationObserver
observer(WebContents());
1160 scoped_ptr
<PromptObserver
> prompt_observer(
1161 PromptObserver::Create(WebContents()));
1162 std::string submit
=
1163 "document.getElementById('username').value = 'temp';"
1164 "document.getElementById('password').value = 'random';"
1165 "document.getElementById('submit-button').click();";
1166 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit
));
1169 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1172 // Test that if there was no previous page load then the PasswordManagerDriver
1173 // does not think that there were SSL errors on the current page. The test opens
1174 // a new tab with a URL for which the embedded test server issues a basic auth
1176 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, NoLastLoadGoodLastLoad
) {
1177 // Teach the embedded server to handle requests by issuing the basic auth
1179 embedded_test_server()->RegisterRequestHandler(
1180 base::Bind(&HandleTestAuthRequest
));
1182 LoginPromptBrowserTestObserver login_observer
;
1183 // We need to register to all sources, because the navigation observer we are
1184 // interested in is for a new tab to be opened, and thus does not exist yet.
1185 login_observer
.Register(content::NotificationService::AllSources());
1187 password_manager::TestPasswordStore
* password_store
=
1188 static_cast<password_manager::TestPasswordStore
*>(
1189 PasswordStoreFactory::GetForProfile(
1190 browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS
).get());
1191 EXPECT_TRUE(password_store
->IsEmpty());
1193 // Navigate to a page requiring HTTP auth. Wait for the tab to get the correct
1194 // WebContents, but don't wait for navigation, which only finishes after
1196 ui_test_utils::NavigateToURLWithDisposition(
1198 embedded_test_server()->GetURL("/basic_auth"),
1200 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB
);
1202 content::NavigationController
* nav_controller
=
1203 &WebContents()->GetController();
1204 NavigationObserver
nav_observer(WebContents());
1205 scoped_ptr
<PromptObserver
> prompt_observer(
1206 PromptObserver::Create(WebContents()));
1207 WindowedAuthNeededObserver
auth_needed_observer(nav_controller
);
1208 auth_needed_observer
.Wait();
1210 WindowedAuthSuppliedObserver
auth_supplied_observer(nav_controller
);
1211 // Offer valid credentials on the auth challenge.
1212 ASSERT_EQ(1u, login_observer
.handlers().size());
1213 LoginHandler
* handler
= *login_observer
.handlers().begin();
1214 ASSERT_TRUE(handler
);
1215 // Any username/password will work.
1216 handler
->SetAuth(base::UTF8ToUTF16("user"), base::UTF8ToUTF16("pwd"));
1217 auth_supplied_observer
.Wait();
1219 // The password manager should be working correctly.
1220 nav_observer
.Wait();
1221 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1222 prompt_observer
->Accept();
1224 // Spin the message loop to make sure the password store had a chance to save
1226 base::RunLoop run_loop
;
1227 run_loop
.RunUntilIdle();
1228 EXPECT_FALSE(password_store
->IsEmpty());
1231 // In some situations, multiple PasswordFormManager instances from
1232 // PasswordManager::pending_login_managers_ would match (via DoesManage) a form
1233 // to be provisionally saved. One of them might be a complete match, the other
1234 // all-but-action match. Normally, the former should be preferred, but if the
1235 // former has not finished matching, and the latter has, the latter should be
1236 // used (otherwise we'd give up even though we could have saved the password).
1237 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1238 PreferPasswordFormManagerWhichFinishedMatching
) {
1239 NavigateToFile("/password/create_form_copy_on_submit.html");
1241 NavigationObserver
observer(WebContents());
1242 scoped_ptr
<PromptObserver
> prompt_observer(
1243 PromptObserver::Create(WebContents()));
1244 std::string submit
=
1245 "document.getElementById('username').value = 'overwrite_me';"
1246 "document.getElementById('password').value = 'random';"
1247 "document.getElementById('non-form-button').click();";
1248 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit
));
1251 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1254 // Test that if login fails and content server pushes a different login form
1255 // with action URL having different schemes. Heuristic shall be able
1256 // identify such cases and *shall not* prompt to save incorrect password.
1257 IN_PROC_BROWSER_TEST_F(
1258 PasswordManagerBrowserTest
,
1259 NoPromptForLoginFailedAndServerPushSeperateLoginForm_HttpToHttps
) {
1261 "/password/separate_login_form_with_onload_submit_script.html";
1262 GURL
http_url(embedded_test_server()->GetURL(path
));
1263 ASSERT_TRUE(http_url
.SchemeIs(url::kHttpScheme
));
1265 NavigationObserver
observer(WebContents());
1266 scoped_ptr
<PromptObserver
> prompt_observer(
1267 PromptObserver::Create(WebContents()));
1268 ui_test_utils::NavigateToURL(browser(), http_url
);
1270 observer
.SetPathToWaitFor("/password/done_and_separate_login_form.html");
1273 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
1276 IN_PROC_BROWSER_TEST_F(
1277 PasswordManagerBrowserTest
,
1278 NoPromptForLoginFailedAndServerPushSeperateLoginForm_HttpsToHttp
) {
1279 base::CommandLine::ForCurrentProcess()->AppendSwitch(
1280 switches::kAllowRunningInsecureContent
);
1281 base::CommandLine::ForCurrentProcess()->AppendSwitch(
1282 switches::kIgnoreCertificateErrors
);
1283 const base::FilePath::CharType kDocRoot
[] =
1284 FILE_PATH_LITERAL("chrome/test/data");
1285 net::SpawnedTestServer
https_test_server(
1286 net::SpawnedTestServer::TYPE_HTTPS
,
1287 net::SpawnedTestServer::SSLOptions(
1288 net::SpawnedTestServer::SSLOptions::CERT_OK
),
1289 base::FilePath(kDocRoot
));
1290 ASSERT_TRUE(https_test_server
.Start());
1292 // This test case cannot inject the scripts via content::ExecuteScript() in
1293 // files served through HTTPS. Therefore the scripts are made part of the HTML
1294 // site and executed on load.
1296 "password/separate_login_form_with_onload_submit_script.html";
1297 GURL
https_url(https_test_server
.GetURL(path
));
1298 ASSERT_TRUE(https_url
.SchemeIs(url::kHttpsScheme
));
1300 NavigationObserver
observer(WebContents());
1301 scoped_ptr
<PromptObserver
> prompt_observer(
1302 PromptObserver::Create(WebContents()));
1303 ui_test_utils::NavigateToURL(browser(), https_url
);
1305 observer
.SetPathToWaitFor("/password/done_and_separate_login_form.html");
1308 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
1311 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1312 PromptWhenPasswordFormWithoutUsernameFieldSubmitted
) {
1313 password_manager::TestPasswordStore
* password_store
=
1314 static_cast<password_manager::TestPasswordStore
*>(
1315 PasswordStoreFactory::GetForProfile(
1316 browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS
).get());
1318 EXPECT_TRUE(password_store
->IsEmpty());
1320 NavigateToFile("/password/form_with_only_password_field.html");
1322 NavigationObserver
observer(WebContents());
1323 scoped_ptr
<PromptObserver
> prompt_observer(
1324 PromptObserver::Create(WebContents()));
1325 std::string submit
=
1326 "document.getElementById('password').value = 'password';"
1327 "document.getElementById('submit-button').click();";
1328 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit
));
1331 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1332 prompt_observer
->Accept();
1334 // Spin the message loop to make sure the password store had a chance to save
1336 base::RunLoop run_loop
;
1337 run_loop
.RunUntilIdle();
1338 EXPECT_FALSE(password_store
->IsEmpty());
1341 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1342 AutofillSuggetionsForPasswordFormWithoutUsernameField
) {
1343 password_manager::TestPasswordStore
* password_store
=
1344 static_cast<password_manager::TestPasswordStore
*>(
1345 PasswordStoreFactory::GetForProfile(
1346 browser()->profile(), ServiceAccessType::IMPLICIT_ACCESS
).get());
1348 EXPECT_TRUE(password_store
->IsEmpty());
1350 // Password form without username-field.
1351 NavigateToFile("/password/form_with_only_password_field.html");
1353 NavigationObserver
observer(WebContents());
1354 scoped_ptr
<PromptObserver
> prompt_observer(
1355 PromptObserver::Create(WebContents()));
1356 std::string submit
=
1357 "document.getElementById('password').value = 'mypassword';"
1358 "document.getElementById('submit-button').click();";
1359 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit
));
1362 prompt_observer
->Accept();
1364 // Spin the message loop to make sure the password store had a chance to save
1366 base::RunLoop run_loop
;
1367 run_loop
.RunUntilIdle();
1368 EXPECT_FALSE(password_store
->IsEmpty());
1370 // Now, navigate to same html password form and verify whether password is
1372 NavigateToFile("/password/form_with_only_password_field.html");
1374 // Let the user interact with the page, so that DOM gets modification events,
1375 // needed for autofilling fields.
1376 content::SimulateMouseClickAt(
1377 WebContents(), 0, blink::WebMouseEvent::ButtonLeft
, gfx::Point(1, 1));
1379 // Wait until that interaction causes the password value to be revealed.
1380 WaitForElementValue("password", "mypassword");
1383 // Test that if a form gets autofilled, then it gets autofilled on re-creation
1385 // TODO(vabr): This is flaky everywhere. http://crbug.com/442704
1386 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1387 DISABLED_ReCreatedFormsGetFilled
) {
1388 NavigateToFile("/password/dynamic_password_form.html");
1390 // Fill in the credentials, and make sure they are saved.
1391 NavigationObserver
form_submit_observer(WebContents());
1392 scoped_ptr
<PromptObserver
> prompt_observer(
1393 PromptObserver::Create(WebContents()));
1394 std::string create_fill_and_submit
=
1395 "document.getElementById('create_form_button').click();"
1396 "window.setTimeout(function() {"
1397 " var form = document.getElementById('dynamic_form_id');"
1398 " form.username.value = 'temp';"
1399 " form.password.value = 'random';"
1402 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), create_fill_and_submit
));
1403 form_submit_observer
.Wait();
1404 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1405 prompt_observer
->Accept();
1407 // Reload the original page to have the saved credentials autofilled.
1408 NavigationObserver
reload_observer(WebContents());
1409 NavigateToFile("/password/dynamic_password_form.html");
1410 reload_observer
.Wait();
1411 std::string create_form
=
1412 "document.getElementById('create_form_button').click();";
1413 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), create_form
));
1414 // Wait until the username is filled, to make sure autofill kicked in.
1415 WaitForElementValue("username_id", "temp");
1417 // Now the form gets deleted and created again. It should get autofilled
1419 std::string delete_form
=
1420 "var form = document.getElementById('dynamic_form_id');"
1421 "form.parentNode.removeChild(form);";
1422 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), delete_form
));
1423 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), create_form
));
1424 WaitForElementValue("username_id", "temp");
1427 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, PromptForPushState
) {
1428 ActivateHistoryPushState();
1429 NavigateToFile("/password/password_push_state.html");
1431 // Verify that we show the save password prompt if 'history.pushState()'
1432 // is called after form submission is suppressed by, for example, calling
1433 // preventDefault in a form's submit event handler.
1434 // Note that calling 'submit()' on a form with javascript doesn't call
1435 // the onsubmit handler, so we click the submit button instead.
1436 NavigationObserver
observer(WebContents());
1437 observer
.SetQuitOnEntryCommitted(true);
1438 scoped_ptr
<PromptObserver
> prompt_observer(
1439 PromptObserver::Create(WebContents()));
1440 std::string fill_and_submit
=
1441 "document.getElementById('username_field').value = 'temp';"
1442 "document.getElementById('password_field').value = 'random';"
1443 "document.getElementById('submit_button').click()";
1444 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
1446 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1449 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1450 InFrameNavigationDoesNotClearPopupState
) {
1451 // Mock out the AutofillClient so we know how long to wait. Unfortunately
1452 // there isn't otherwise a good even to wait on to verify that the popup
1453 // would have been shown.
1454 password_manager::ContentPasswordManagerDriverFactory
* driver_factory
=
1455 password_manager::ContentPasswordManagerDriverFactory::FromWebContents(
1457 ObservingAutofillClient observing_autofill_client
;
1458 driver_factory
->TestingSetDriverForFrame(
1459 RenderViewHost()->GetMainFrame(),
1460 make_scoped_ptr(new password_manager::ContentPasswordManagerDriver(
1461 RenderViewHost()->GetMainFrame(),
1462 ChromePasswordManagerClient::FromWebContents(WebContents()),
1463 &observing_autofill_client
)));
1465 NavigateToFile("/password/password_form.html");
1467 NavigationObserver
form_submit_observer(WebContents());
1468 scoped_ptr
<PromptObserver
> prompt_observer(
1469 PromptObserver::Create(WebContents()));
1471 "document.getElementById('username_field').value = 'temp';"
1472 "document.getElementById('password_field').value = 'random123';"
1473 "document.getElementById('input_submit_button').click();";
1475 // Save credentials for the site.
1476 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill
));
1477 form_submit_observer
.Wait();
1478 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1479 prompt_observer
->Accept();
1481 NavigateToFile("/password/password_form.html");
1482 ASSERT_TRUE(content::ExecuteScript(
1484 "var usernameRect = document.getElementById('username_field')"
1485 ".getBoundingClientRect();"));
1487 // Trigger in page navigation.
1488 std::string in_page_navigate
= "location.hash = '#blah';";
1489 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), in_page_navigate
));
1491 // Click on the username field to display the popup.
1493 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
1495 "window.domAutomationController.send(usernameRect.top);",
1498 ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
1500 "window.domAutomationController.send(usernameRect.left);",
1503 content::SimulateMouseClickAt(
1504 WebContents(), 0, blink::WebMouseEvent::ButtonLeft
, gfx::Point(left
+ 1,
1506 // Make sure the popup would be shown.
1507 observing_autofill_client
.Wait();
1510 // Passwords from change password forms should only be offered for saving when
1511 // it is certain that the username is correct.
1512 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, ChangePwdCorrect
) {
1513 NavigateToFile("/password/password_form.html");
1515 NavigationObserver
observer(WebContents());
1516 scoped_ptr
<PromptObserver
> prompt_observer(
1517 PromptObserver::Create(WebContents()));
1518 std::string fill_and_submit
=
1519 "document.getElementById('mark_chg_username_field').value = 'temp';"
1520 "document.getElementById('mark_chg_password_field').value = 'random';"
1521 "document.getElementById('mark_chg_new_password_1').value = 'random1';"
1522 "document.getElementById('mark_chg_new_password_2').value = 'random1';"
1523 "document.getElementById('mark_chg_submit_button').click()";
1524 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
1526 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1529 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, ChangePwdIncorrect
) {
1530 NavigateToFile("/password/password_form.html");
1532 NavigationObserver
observer(WebContents());
1533 scoped_ptr
<PromptObserver
> prompt_observer(
1534 PromptObserver::Create(WebContents()));
1535 std::string fill_and_submit
=
1536 "document.getElementById('chg_not_username_field').value = 'temp';"
1537 "document.getElementById('chg_password_field').value = 'random';"
1538 "document.getElementById('chg_new_password_1').value = 'random1';"
1539 "document.getElementById('chg_new_password_2').value = 'random1';"
1540 "document.getElementById('chg_submit_button').click()";
1541 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
1543 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
1546 // As the two ChangePwd* tests above, only with submitting through
1547 // history.pushState().
1548 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, ChangePwdPushStateCorrect
) {
1549 ActivateHistoryPushState();
1550 NavigateToFile("/password/password_push_state.html");
1552 NavigationObserver
observer(WebContents());
1553 observer
.SetQuitOnEntryCommitted(true);
1554 scoped_ptr
<PromptObserver
> prompt_observer(
1555 PromptObserver::Create(WebContents()));
1556 std::string fill_and_submit
=
1557 "document.getElementById('mark_chg_username_field').value = 'temp';"
1558 "document.getElementById('mark_chg_password_field').value = 'random';"
1559 "document.getElementById('mark_chg_new_password_1').value = 'random1';"
1560 "document.getElementById('mark_chg_new_password_2').value = 'random1';"
1561 "document.getElementById('mark_chg_submit_button').click()";
1562 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
1564 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1567 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1568 ChangePwdPushStateIncorrect
) {
1569 ActivateHistoryPushState();
1570 NavigateToFile("/password/password_push_state.html");
1572 NavigationObserver
observer(WebContents());
1573 observer
.SetQuitOnEntryCommitted(true);
1574 scoped_ptr
<PromptObserver
> prompt_observer(
1575 PromptObserver::Create(WebContents()));
1576 std::string fill_and_submit
=
1577 "document.getElementById('chg_not_username_field').value = 'temp';"
1578 "document.getElementById('chg_password_field').value = 'random';"
1579 "document.getElementById('chg_new_password_1').value = 'random1';"
1580 "document.getElementById('chg_new_password_2').value = 'random1';"
1581 "document.getElementById('chg_submit_button').click()";
1582 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
1584 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
1587 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
, NoPromptOnBack
) {
1588 // Go to a successful landing page through submitting first, so that it is
1589 // reachable through going back, and the remembered page transition is form
1590 // submit. There is no need to submit non-empty strings.
1591 NavigateToFile("/password/password_form.html");
1593 NavigationObserver
dummy_submit_observer(WebContents());
1594 std::string just_submit
=
1595 "document.getElementById('input_submit_button').click()";
1596 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), just_submit
));
1597 dummy_submit_observer
.Wait();
1599 // Now go to a page with a form again, fill the form, and go back instead of
1601 NavigateToFile("/password/dummy_submit.html");
1603 NavigationObserver
observer(WebContents());
1604 scoped_ptr
<PromptObserver
> prompt_observer(
1605 PromptObserver::Create(WebContents()));
1606 // The (dummy) submit is necessary to provisionally save the typed password. A
1607 // user typing in the password field would not need to submit to provisionally
1608 // save it, but the script cannot trigger that just by assigning to the
1610 std::string fill_and_back
=
1611 "document.getElementById('password_field').value = 'random';"
1612 "document.getElementById('input_submit_button').click();"
1613 "window.history.back();";
1614 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_back
));
1616 EXPECT_FALSE(prompt_observer
->IsShowingPrompt());
1619 // Regression test for http://crbug.com/452306
1620 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1621 ChangingTextToPasswordFieldOnSignupForm
) {
1622 NavigateToFile("/password/signup_form.html");
1624 // In this case, pretend that username_field is actually a password field
1625 // that starts as a text field to simulate placeholder.
1626 NavigationObserver
observer(WebContents());
1627 scoped_ptr
<PromptObserver
> prompt_observer(
1628 PromptObserver::Create(WebContents()));
1629 std::string change_and_submit
=
1630 "document.getElementById('other_info').value = 'username';"
1631 "document.getElementById('username_field').type = 'password';"
1632 "document.getElementById('username_field').value = 'mypass';"
1633 "document.getElementById('password_field').value = 'mypass';"
1634 "document.getElementById('testform').submit();";
1635 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), change_and_submit
));
1637 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());
1640 // Regression test for http://crbug.com/451631
1641 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest
,
1642 SavingOnManyPasswordFieldsTest
) {
1643 // Simulate Macy's registration page, which contains the normal 2 password
1644 // fields for confirming the new password plus 2 more fields for security
1645 // questions and credit card. Make sure that saving works correctly for such
1647 NavigateToFile("/password/many_password_signup_form.html");
1649 NavigationObserver
observer(WebContents());
1650 scoped_ptr
<PromptObserver
> prompt_observer(
1651 PromptObserver::Create(WebContents()));
1652 std::string fill_and_submit
=
1653 "document.getElementById('username_field').value = 'username';"
1654 "document.getElementById('password_field').value = 'mypass';"
1655 "document.getElementById('confirm_field').value = 'mypass';"
1656 "document.getElementById('security_answer').value = 'hometown';"
1657 "document.getElementById('SSN').value = '1234';"
1658 "document.getElementById('testform').submit();";
1659 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit
));
1661 EXPECT_TRUE(prompt_observer
->IsShowingPrompt());