Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / password_manager / password_manager_browsertest.cc
blob74f026a3a1fd53bc83821dd7b4b9f84b905bca88
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.
5 #include <string>
7 #include "base/command_line.h"
8 #include "base/metrics/histogram_samples.h"
9 #include "base/metrics/statistics_recorder.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
12 #include "chrome/browser/infobars/infobar.h"
13 #include "chrome/browser/infobars/infobar_service.h"
14 #include "chrome/browser/password_manager/password_store_factory.h"
15 #include "chrome/browser/password_manager/test_password_store.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "chrome/test/base/in_process_browser_test.h"
19 #include "chrome/test/base/test_switches.h"
20 #include "chrome/test/base/ui_test_utils.h"
21 #include "components/autofill/core/browser/autofill_test_utils.h"
22 #include "content/public/browser/notification_observer.h"
23 #include "content/public/browser/notification_registrar.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "content/public/browser/web_contents.h"
27 #include "content/public/browser/web_contents_observer.h"
28 #include "content/public/test/browser_test_utils.h"
29 #include "content/public/test/test_utils.h"
30 #include "net/test/embedded_test_server/embedded_test_server.h"
31 #include "net/url_request/test_url_fetcher_factory.h"
32 #include "testing/gmock/include/gmock/gmock.h"
33 #include "ui/events/keycodes/keyboard_codes.h"
36 // NavigationObserver ---------------------------------------------------------
38 namespace {
40 // Observer that waits for navigation to complete and for the password infobar
41 // to be shown.
42 class NavigationObserver : public content::NotificationObserver,
43 public content::WebContentsObserver {
44 public:
45 explicit NavigationObserver(content::WebContents* web_contents)
46 : content::WebContentsObserver(web_contents),
47 message_loop_runner_(new content::MessageLoopRunner),
48 infobar_shown_(false),
49 infobar_removed_(false),
50 should_automatically_accept_infobar_(true),
51 infobar_service_(InfoBarService::FromWebContents(web_contents)) {
52 registrar_.Add(this,
53 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
54 content::Source<InfoBarService>(infobar_service_));
55 registrar_.Add(this,
56 chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
57 content::Source<InfoBarService>(infobar_service_));
60 virtual ~NavigationObserver() {}
62 // Normally Wait() will not return until a main frame navigation occurs.
63 // If a path is set, Wait() will return after this path has been seen,
64 // regardless of the frame that navigated. Useful for multi-frame pages.
65 void SetPathToWaitFor(const std::string& path) {
66 wait_for_path_ = path;
69 // content::NotificationObserver:
70 virtual void Observe(int type,
71 const content::NotificationSource& source,
72 const content::NotificationDetails& details) OVERRIDE {
73 switch (type) {
74 case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED:
75 if (should_automatically_accept_infobar_) {
76 infobar_service_->infobar_at(0)
77 ->delegate()
78 ->AsConfirmInfoBarDelegate()
79 ->Accept();
81 infobar_shown_ = true;
82 return;
83 case chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED:
84 infobar_removed_ = true;
85 return;
86 default:
87 NOTREACHED();
88 return;
92 // content::WebContentsObserver:
93 virtual void DidFinishLoad(
94 int64 frame_id,
95 const GURL& validated_url,
96 bool is_main_frame,
97 content::RenderViewHost* render_view_host) OVERRIDE {
98 if (!wait_for_path_.empty()) {
99 if (validated_url.path() == wait_for_path_)
100 message_loop_runner_->Quit();
101 } else if (is_main_frame) {
102 message_loop_runner_->Quit();
106 bool infobar_shown() const { return infobar_shown_; }
107 bool infobar_removed() const { return infobar_removed_; }
109 void disable_should_automatically_accept_infobar() {
110 should_automatically_accept_infobar_ = false;
113 void Wait() {
114 message_loop_runner_->Run();
117 private:
118 std::string wait_for_path_;
119 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
120 bool infobar_shown_;
121 bool infobar_removed_;
122 // If |should_automatically_accept_infobar_| is true, then whenever the test
123 // sees an infobar added, it will click its accepting button. Default = true.
124 bool should_automatically_accept_infobar_;
125 content::NotificationRegistrar registrar_;
126 InfoBarService* infobar_service_;
128 DISALLOW_COPY_AND_ASSIGN(NavigationObserver);
131 } // namespace
134 // PasswordManagerBrowserTest -------------------------------------------------
136 class PasswordManagerBrowserTest : public InProcessBrowserTest {
137 public:
138 PasswordManagerBrowserTest() {}
139 virtual ~PasswordManagerBrowserTest() {}
141 // InProcessBrowserTest:
142 virtual void SetUpOnMainThread() OVERRIDE {
143 // Use TestPasswordStore to remove a possible race. Normally the
144 // PasswordStore does its database manipulation on the DB thread, which
145 // creates a possible race during navigation. Specifically the
146 // PasswordManager will ignore any forms in a page if the load from the
147 // PasswordStore has not completed.
148 PasswordStoreFactory::GetInstance()->SetTestingFactory(
149 browser()->profile(), &TestPasswordStore::Create);
152 protected:
153 content::WebContents* WebContents() {
154 return browser()->tab_strip_model()->GetActiveWebContents();
157 content::RenderViewHost* RenderViewHost() {
158 return WebContents()->GetRenderViewHost();
161 // Wrapper around ui_test_utils::NavigateToURL that waits until
162 // DidFinishLoad() fires. Normally this function returns after
163 // DidStopLoading(), which caused flakiness as the NavigationObserver
164 // would sometimes see the DidFinishLoad event from a previous navigation and
165 // return immediately.
166 void NavigateToFile(const std::string& path) {
167 if (!embedded_test_server()->Started())
168 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
170 NavigationObserver observer(WebContents());
171 GURL url = embedded_test_server()->GetURL(path);
172 ui_test_utils::NavigateToURL(browser(), url);
173 observer.Wait();
176 private:
177 DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest);
180 // Actual tests ---------------------------------------------------------------
181 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
182 PromptForNormalSubmit) {
183 NavigateToFile("/password/password_form.html");
185 // Fill a form and submit through a <input type="submit"> button. Nothing
186 // special.
187 NavigationObserver observer(WebContents());
188 std::string fill_and_submit =
189 "document.getElementById('username_field').value = 'temp';"
190 "document.getElementById('password_field').value = 'random';"
191 "document.getElementById('input_submit_button').click()";
192 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
193 observer.Wait();
194 EXPECT_TRUE(observer.infobar_shown());
197 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
198 PromptForSubmitWithInPageNavigation) {
199 NavigateToFile("/password/password_navigate_before_submit.html");
201 // Fill a form and submit through a <input type="submit"> button. Nothing
202 // special. The form does an in-page navigation before submitting.
203 NavigationObserver observer(WebContents());
204 std::string fill_and_submit =
205 "document.getElementById('username_field').value = 'temp';"
206 "document.getElementById('password_field').value = 'random';"
207 "document.getElementById('input_submit_button').click()";
208 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
209 observer.Wait();
210 EXPECT_TRUE(observer.infobar_shown());
213 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
214 LoginSuccessWithUnrelatedForm) {
215 // Log in, see a form on the landing page. That form is not related to the
216 // login form (=has a different action), so we should offer saving the
217 // password.
218 NavigateToFile("/password/password_form.html");
220 NavigationObserver observer(WebContents());
221 std::string fill_and_submit =
222 "document.getElementById('username_unrelated').value = 'temp';"
223 "document.getElementById('password_unrelated').value = 'random';"
224 "document.getElementById('submit_unrelated').click()";
225 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
226 observer.Wait();
227 EXPECT_TRUE(observer.infobar_shown());
230 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, LoginFailed) {
231 // Log in, see a form on the landing page. That form is not related to the
232 // login form (=has a different action), so we should offer saving the
233 // password.
234 NavigateToFile("/password/password_form.html");
236 NavigationObserver observer(WebContents());
237 std::string fill_and_submit =
238 "document.getElementById('username_failed').value = 'temp';"
239 "document.getElementById('password_failed').value = 'random';"
240 "document.getElementById('submit_failed').click()";
241 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
242 observer.Wait();
243 EXPECT_FALSE(observer.infobar_shown());
246 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, Redirects) {
247 NavigateToFile("/password/password_form.html");
249 // Fill a form and submit through a <input type="submit"> button. The form
250 // points to a redirection page.
251 NavigationObserver observer(WebContents());
252 std::string fill_and_submit =
253 "document.getElementById('username_redirect').value = 'temp';"
254 "document.getElementById('password_redirect').value = 'random';"
255 "document.getElementById('submit_redirect').click()";
256 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
257 observer.disable_should_automatically_accept_infobar();
258 observer.Wait();
259 EXPECT_TRUE(observer.infobar_shown());
261 // The redirection page now redirects via Javascript. We check that the
262 // infobar stays.
263 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
264 "window.location.href = 'done.html';"));
265 observer.Wait();
266 EXPECT_FALSE(observer.infobar_removed());
269 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
270 PromptForSubmitUsingJavaScript) {
271 NavigateToFile("/password/password_form.html");
273 // Fill a form and submit using <button> that calls submit() on the form.
274 // This should work regardless of the type of element, as long as submit() is
275 // called.
276 NavigationObserver observer(WebContents());
277 std::string fill_and_submit =
278 "document.getElementById('username_field').value = 'temp';"
279 "document.getElementById('password_field').value = 'random';"
280 "document.getElementById('submit_button').click()";
281 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
282 observer.Wait();
283 EXPECT_TRUE(observer.infobar_shown());
286 // Flaky: crbug.com/301547, observed on win and mac. Probably happens on all
287 // platforms.
288 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
289 DISABLED_PromptForDynamicForm) {
290 NavigateToFile("/password/dynamic_password_form.html");
292 // Fill the dynamic password form and submit.
293 NavigationObserver observer(WebContents());
294 std::string fill_and_submit =
295 "document.getElementById('create_form_button').click();"
296 "window.setTimeout(function() {"
297 " document.dynamic_form.username.value = 'tempro';"
298 " document.dynamic_form.password.value = 'random';"
299 " document.dynamic_form.submit();"
300 "}, 0)";
301 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
302 observer.Wait();
303 EXPECT_TRUE(observer.infobar_shown());
306 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForNavigation) {
307 NavigateToFile("/password/password_form.html");
309 // Don't fill the password form, just navigate away. Shouldn't prompt.
310 NavigationObserver observer(WebContents());
311 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
312 "window.location.href = 'done.html';"));
313 observer.Wait();
314 EXPECT_FALSE(observer.infobar_shown());
317 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
318 NoPromptForSubFrameNavigation) {
319 NavigateToFile("/password/multi_frames.html");
321 // If you are filling out a password form in one frame and a different frame
322 // navigates, this should not trigger the infobar.
323 NavigationObserver observer(WebContents());
324 observer.SetPathToWaitFor("/password/done.html");
325 std::string fill =
326 "var first_frame = document.getElementById('first_frame');"
327 "var frame_doc = first_frame.contentDocument;"
328 "frame_doc.getElementById('username_field').value = 'temp';"
329 "frame_doc.getElementById('password_field').value = 'random';";
330 std::string navigate_frame =
331 "var second_iframe = document.getElementById('second_frame');"
332 "second_iframe.contentWindow.location.href = 'done.html';";
334 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill));
335 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
336 observer.Wait();
337 EXPECT_FALSE(observer.infobar_shown());
340 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
341 PromptAfterSubmitWithSubFrameNavigation) {
342 NavigateToFile("/password/multi_frames.html");
344 // Make sure that we prompt to save password even if a sub-frame navigation
345 // happens first.
346 NavigationObserver observer(WebContents());
347 observer.SetPathToWaitFor("/password/done.html");
348 std::string navigate_frame =
349 "var second_iframe = document.getElementById('second_frame');"
350 "second_iframe.contentWindow.location.href = 'other.html';";
351 std::string fill_and_submit =
352 "var first_frame = document.getElementById('first_frame');"
353 "var frame_doc = first_frame.contentDocument;"
354 "frame_doc.getElementById('username_field').value = 'temp';"
355 "frame_doc.getElementById('password_field').value = 'random';"
356 "frame_doc.getElementById('input_submit_button').click();";
358 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
359 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
360 observer.Wait();
361 EXPECT_TRUE(observer.infobar_shown());
364 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
365 PromptForXHRSubmit) {
366 #if defined(OS_WIN) && defined(USE_ASH)
367 // Disable this test in Metro+Ash for now (http://crbug.com/262796).
368 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
369 return;
370 #endif
371 NavigateToFile("/password/password_xhr_submit.html");
373 // Verify that we show the save password prompt if a form returns false
374 // in its onsubmit handler but instead logs in/navigates via XHR.
375 // Note that calling 'submit()' on a form with javascript doesn't call
376 // the onsubmit handler, so we click the submit button instead.
377 NavigationObserver observer(WebContents());
378 std::string fill_and_submit =
379 "document.getElementById('username_field').value = 'temp';"
380 "document.getElementById('password_field').value = 'random';"
381 "document.getElementById('submit_button').click()";
382 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
383 observer.Wait();
384 EXPECT_TRUE(observer.infobar_shown());
387 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForOtherXHR) {
388 NavigateToFile("/password/password_xhr_submit.html");
390 // Verify that if random XHR navigation occurs, we don't try and save the
391 // password.
393 // We may want to change this functionality in the future to account for
394 // cases where the element that users click on isn't a submit button.
395 NavigationObserver observer(WebContents());
396 std::string fill_and_navigate =
397 "document.getElementById('username_field').value = 'temp';"
398 "document.getElementById('password_field').value = 'random';"
399 "send_xhr()";
400 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate));
401 observer.Wait();
402 EXPECT_FALSE(observer.infobar_shown());
405 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
406 VerifyPasswordGenerationUpload) {
407 // Prevent Autofill requests from actually going over the wire.
408 net::TestURLFetcherFactory factory;
409 // Disable Autofill requesting access to AddressBook data. This causes
410 // the test to hang on Mac.
411 autofill::test::DisableSystemServices(browser()->profile());
413 // Visit a signup form.
414 NavigateToFile("/password/signup_form.html");
416 // Enter a password and save it.
417 NavigationObserver first_observer(WebContents());
418 std::string fill_and_submit =
419 "document.getElementById('other_info').value = 'stuff';"
420 "document.getElementById('username_field').value = 'my_username';"
421 "document.getElementById('password_field').value = 'password';"
422 "document.getElementById('input_submit_button').click()";
423 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
425 first_observer.Wait();
426 ASSERT_TRUE(first_observer.infobar_shown());
428 // Now navigate to a login form that has similar HTML markup.
429 NavigateToFile("/password/password_form.html");
431 // Simulate a user click to force an autofill of the form's DOM value, not
432 // just the suggested value.
433 std::string click = "document.getElementById('testform_no_name').click()";
434 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), click));
436 // The form should be filled with the previously submitted username.
437 std::string get_username =
438 "window.domAutomationController.send("
439 "document.getElementById('username_field').value);";
440 std::string actual_username;
441 ASSERT_TRUE(content::ExecuteScriptAndExtractString(RenderViewHost(),
442 get_username,
443 &actual_username));
444 ASSERT_EQ("my_username", actual_username);
446 // Submit the form and verify that there is no infobar (as the password
447 // has already been saved).
448 NavigationObserver second_observer(WebContents());
449 std::string submit_form =
450 "document.getElementById('input_submit_button').click()";
451 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit_form));
452 second_observer.Wait();
453 EXPECT_FALSE(second_observer.infobar_shown());
455 // Verify that we sent a ping to Autofill saying that the original form
456 // was likely an account creation form since it has more than 2 text input
457 // fields and was used for the first time on a different form.
458 base::HistogramBase* upload_histogram =
459 base::StatisticsRecorder::FindHistogram(
460 "PasswordGeneration.UploadStarted");
461 ASSERT_TRUE(upload_histogram);
462 scoped_ptr<base::HistogramSamples> snapshot =
463 upload_histogram->SnapshotSamples();
464 EXPECT_EQ(0, snapshot->GetCount(0 /* failure */));
465 EXPECT_EQ(1, snapshot->GetCount(1 /* success */));
468 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForSubmitFromIframe) {
469 NavigateToFile("/password/password_submit_from_iframe.html");
471 // Submit a form in an iframe, then cause the whole page to navigate without a
472 // user gesture. We expect the save password prompt to be shown here, because
473 // some pages use such iframes for login forms.
474 NavigationObserver observer(WebContents());
475 std::string fill_and_submit =
476 "var iframe = document.getElementById('test_iframe');"
477 "var iframe_doc = iframe.contentDocument;"
478 "iframe_doc.getElementById('username_field').value = 'temp';"
479 "iframe_doc.getElementById('password_field').value = 'random';"
480 "iframe_doc.getElementById('submit_button').click()";
482 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
483 observer.Wait();
484 EXPECT_TRUE(observer.infobar_shown());
487 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
488 PromptForInputElementWithoutName) {
489 // Check that the prompt is shown for forms where input elements lack the
490 // "name" attribute but the "id" is present.
491 NavigateToFile("/password/password_form.html");
493 NavigationObserver observer(WebContents());
494 std::string fill_and_submit =
495 "document.getElementById('username_field_no_name').value = 'temp';"
496 "document.getElementById('password_field_no_name').value = 'random';"
497 "document.getElementById('input_submit_button_no_name').click()";
498 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
499 observer.Wait();
500 EXPECT_TRUE(observer.infobar_shown());
503 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
504 PromptForInputElementWithoutId) {
505 // Check that the prompt is shown for forms where input elements lack the
506 // "id" attribute but the "name" attribute is present.
507 NavigateToFile("/password/password_form.html");
509 NavigationObserver observer(WebContents());
510 std::string fill_and_submit =
511 "document.getElementsByName('username_field_no_id')[0].value = 'temp';"
512 "document.getElementsByName('password_field_no_id')[0].value = 'random';"
513 "document.getElementsByName('input_submit_button_no_id')[0].click()";
514 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
515 observer.Wait();
516 EXPECT_TRUE(observer.infobar_shown());
519 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
520 NoPromptForInputElementWithoutIdAndName) {
521 // Check that no prompt is shown for forms where the input fields lack both
522 // the "id" and the "name" attributes.
523 NavigateToFile("/password/password_form.html");
525 NavigationObserver observer(WebContents());
526 std::string fill_and_submit =
527 "var form = document.getElementById('testform_elements_no_id_no_name');"
528 "var username = form.children[0];"
529 "username.value = 'temp';"
530 "var password = form.children[1];"
531 "password.value = 'random';"
532 "form.children[2].click()"; // form.children[2] is the submit button.
533 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
534 observer.Wait();
535 EXPECT_FALSE(observer.infobar_shown());
538 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, DeleteFrameBeforeSubmit) {
539 NavigateToFile("/password/multi_frames.html");
541 NavigationObserver observer(WebContents());
542 // Make sure we save some password info from an iframe and then destroy it.
543 std::string save_and_remove =
544 "var first_frame = document.getElementById('first_frame');"
545 "var frame_doc = first_frame.contentDocument;"
546 "frame_doc.getElementById('username_field').value = 'temp';"
547 "frame_doc.getElementById('password_field').value = 'random';"
548 "frame_doc.getElementById('input_submit_button').click();"
549 "first_frame.parentNode.removeChild(first_frame);";
550 // Submit from the main frame, but without navigating through the onsubmit
551 // handler.
552 std::string navigate_frame =
553 "document.getElementById('username_field').value = 'temp';"
554 "document.getElementById('password_field').value = 'random';"
555 "document.getElementById('input_submit_button').click();"
556 "window.location.href = 'done.html';";
558 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), save_and_remove));
559 ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
560 observer.Wait();
561 // The only thing we check here is that there is no use-after-free reported.