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.
4
5#include <string>
6
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/ui/browser.h"
21#include "chrome/browser/ui/login/login_prompt.h"
22#include "chrome/browser/ui/login/login_prompt_test_utils.h"
23#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
24#include "chrome/browser/ui/tabs/tab_strip_model.h"
25#include "chrome/common/chrome_paths.h"
26#include "chrome/common/chrome_switches.h"
27#include "chrome/common/chrome_version_info.h"
28#include "chrome/test/base/in_process_browser_test.h"
29#include "chrome/test/base/test_switches.h"
30#include "chrome/test/base/ui_test_utils.h"
31#include "components/autofill/core/browser/autofill_test_utils.h"
32#include "components/infobars/core/confirm_infobar_delegate.h"
33#include "components/infobars/core/infobar.h"
34#include "components/infobars/core/infobar_manager.h"
35#include "components/password_manager/core/browser/test_password_store.h"
36#include "components/password_manager/core/common/password_manager_switches.h"
37#include "content/public/browser/navigation_controller.h"
38#include "content/public/browser/notification_service.h"
39#include "content/public/browser/render_frame_host.h"
40#include "content/public/browser/render_view_host.h"
41#include "content/public/browser/web_contents.h"
42#include "content/public/browser/web_contents_observer.h"
43#include "content/public/common/content_switches.h"
44#include "content/public/test/browser_test_utils.h"
45#include "content/public/test/test_utils.h"
46#include "net/base/filename_util.h"
47#include "net/test/embedded_test_server/embedded_test_server.h"
48#include "net/test/embedded_test_server/http_request.h"
49#include "net/test/embedded_test_server/http_response.h"
50#include "net/test/spawned_test_server/spawned_test_server.h"
51#include "net/url_request/test_url_fetcher_factory.h"
52#include "testing/gmock/include/gmock/gmock.h"
53#include "third_party/WebKit/public/web/WebInputEvent.h"
54#include "ui/events/keycodes/keyboard_codes.h"
55#include "ui/gfx/geometry/point.h"
56
57
58// NavigationObserver ---------------------------------------------------------
59
60namespace {
61
62// Observer that waits for navigation to complete and for the password infobar
63// to be shown.
64class NavigationObserver : public content::WebContentsObserver {
65 public:
66  explicit NavigationObserver(content::WebContents* web_contents)
67      : content::WebContentsObserver(web_contents),
68        message_loop_runner_(new content::MessageLoopRunner) {}
69
70  virtual ~NavigationObserver() {}
71
72  // Normally Wait() will not return until a main frame navigation occurs.
73  // If a path is set, Wait() will return after this path has been seen,
74  // regardless of the frame that navigated. Useful for multi-frame pages.
75  void SetPathToWaitFor(const std::string& path) {
76    wait_for_path_ = path;
77  }
78
79  // content::WebContentsObserver:
80  virtual void DidFinishLoad(content::RenderFrameHost* render_frame_host,
81                             const GURL& validated_url) OVERRIDE {
82    if (!wait_for_path_.empty()) {
83      if (validated_url.path() == wait_for_path_)
84        message_loop_runner_->Quit();
85    } else if (!render_frame_host->GetParent()) {
86      message_loop_runner_->Quit();
87    }
88  }
89
90  void Wait() { message_loop_runner_->Run(); }
91
92 private:
93  std::string wait_for_path_;
94  scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
95
96  DISALLOW_COPY_AND_ASSIGN(NavigationObserver);
97};
98
99// Observes the save password prompt (bubble or infobar) for a specified
100// WebContents, keeps track of whether or not it is currently shown, and allows
101// accepting saving passwords through it.
102class PromptObserver {
103 public:
104  virtual ~PromptObserver() {}
105
106  // Checks if the prompt is being currently shown.
107  virtual bool IsShowingPrompt() const = 0;
108
109  // Expecting that the prompt is shown, saves the password. Checks that the
110  // prompt is no longer visible afterwards.
111  void Accept() const {
112    EXPECT_TRUE(IsShowingPrompt());
113    AcceptImpl();
114  }
115
116  // Chooses the right implementation of PromptObserver and creates an instance
117  // of it.
118  static scoped_ptr<PromptObserver> Create(content::WebContents* web_contents);
119
120 protected:
121  PromptObserver() {}
122
123  // Accepts the password. The implementation can assume that the prompt is
124  // currently shown, but is required to verify that the prompt is eventually
125  // closed.
126  virtual void AcceptImpl() const = 0;
127
128 private:
129  DISALLOW_COPY_AND_ASSIGN(PromptObserver);
130};
131
132class InfoBarObserver : public PromptObserver,
133                        public infobars::InfoBarManager::Observer {
134 public:
135  explicit InfoBarObserver(content::WebContents* web_contents)
136      : infobar_is_being_shown_(false),
137        infobar_service_(InfoBarService::FromWebContents(web_contents)) {
138    infobar_service_->AddObserver(this);
139  }
140
141  virtual ~InfoBarObserver() {
142    if (infobar_service_)
143      infobar_service_->RemoveObserver(this);
144  }
145
146 private:
147  // PromptObserver:
148  virtual bool IsShowingPrompt() const OVERRIDE {
149    return infobar_is_being_shown_;
150  }
151
152  virtual void AcceptImpl() const OVERRIDE {
153    EXPECT_EQ(1u, infobar_service_->infobar_count());
154    if (!infobar_service_->infobar_count())
155      return;  // Let the test finish to gather possibly more diagnostics.
156
157    // ConfirmInfoBarDelegate::Accept returning true means the infobar is
158    // immediately closed. Checking the return value is preferred to testing
159    // IsShowingPrompt() here, for it avoids the delay until the closing
160    // notification is received.
161    EXPECT_TRUE(infobar_service_->infobar_at(0)
162                    ->delegate()
163                    ->AsConfirmInfoBarDelegate()
164                    ->Accept());
165  }
166
167  // infobars::InfoBarManager::Observer:
168  virtual void OnInfoBarAdded(infobars::InfoBar* infobar) OVERRIDE {
169    infobar_is_being_shown_ = true;
170  }
171
172  virtual void OnInfoBarRemoved(infobars::InfoBar* infobar,
173                                bool animate) OVERRIDE {
174    infobar_is_being_shown_ = false;
175  }
176
177  virtual void OnManagerShuttingDown(
178      infobars::InfoBarManager* manager) OVERRIDE {
179    ASSERT_EQ(infobar_service_, manager);
180    infobar_service_->RemoveObserver(this);
181    infobar_service_ = NULL;
182  }
183
184  bool infobar_is_being_shown_;
185  InfoBarService* infobar_service_;
186
187  DISALLOW_COPY_AND_ASSIGN(InfoBarObserver);
188};
189
190class BubbleObserver : public PromptObserver {
191 public:
192  explicit BubbleObserver(content::WebContents* web_contents)
193      : ui_controller_(
194            ManagePasswordsUIController::FromWebContents(web_contents)) {}
195
196  virtual ~BubbleObserver() {}
197
198 private:
199  // PromptObserver:
200  virtual bool IsShowingPrompt() const OVERRIDE {
201    return ui_controller_->PasswordPendingUserDecision();
202  }
203
204  virtual void AcceptImpl() const OVERRIDE {
205    ui_controller_->SavePassword();
206    EXPECT_FALSE(IsShowingPrompt());
207  }
208
209  ManagePasswordsUIController* const ui_controller_;
210
211  DISALLOW_COPY_AND_ASSIGN(BubbleObserver);
212};
213
214GURL GetFileURL(const char* filename) {
215  base::FilePath path;
216  PathService::Get(chrome::DIR_TEST_DATA, &path);
217  path = path.AppendASCII("password").AppendASCII(filename);
218  CHECK(base::PathExists(path));
219  return net::FilePathToFileURL(path);
220}
221
222// static
223scoped_ptr<PromptObserver> PromptObserver::Create(
224    content::WebContents* web_contents) {
225  if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
226    return scoped_ptr<PromptObserver>(new BubbleObserver(web_contents));
227  } else {
228    return scoped_ptr<PromptObserver>(new InfoBarObserver(web_contents));
229  }
230}
231
232// Handles |request| to "/basic_auth". If "Authorization" header is present,
233// responds with a non-empty HTTP 200 page (regardless of its value). Otherwise
234// serves a Basic Auth challenge.
235scoped_ptr<net::test_server::HttpResponse> HandleTestAuthRequest(
236    const net::test_server::HttpRequest& request) {
237  if (!StartsWithASCII(request.relative_url, "/basic_auth", true))
238    return scoped_ptr<net::test_server::HttpResponse>();
239
240  if (ContainsKey(request.headers, "Authorization")) {
241    scoped_ptr<net::test_server::BasicHttpResponse> http_response(
242        new net::test_server::BasicHttpResponse);
243    http_response->set_code(net::HTTP_OK);
244    http_response->set_content("Success!");
245    return http_response.PassAs<net::test_server::HttpResponse>();
246  } else {
247    scoped_ptr<net::test_server::BasicHttpResponse> http_response(
248        new net::test_server::BasicHttpResponse);
249    http_response->set_code(net::HTTP_UNAUTHORIZED);
250    http_response->AddCustomHeader("WWW-Authenticate",
251                                   "Basic realm=\"test realm\"");
252    return http_response.PassAs<net::test_server::HttpResponse>();
253  }
254}
255
256}  // namespace
257
258
259// PasswordManagerBrowserTest -------------------------------------------------
260
261class PasswordManagerBrowserTest : public InProcessBrowserTest {
262 public:
263  PasswordManagerBrowserTest() {}
264  virtual ~PasswordManagerBrowserTest() {}
265
266  // InProcessBrowserTest:
267  virtual void SetUpOnMainThread() OVERRIDE {
268    // Use TestPasswordStore to remove a possible race. Normally the
269    // PasswordStore does its database manipulation on the DB thread, which
270    // creates a possible race during navigation. Specifically the
271    // PasswordManager will ignore any forms in a page if the load from the
272    // PasswordStore has not completed.
273    PasswordStoreFactory::GetInstance()->SetTestingFactory(
274        browser()->profile(), TestPasswordStoreService::Build);
275    ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
276    ASSERT_FALSE(CommandLine::ForCurrentProcess()->HasSwitch(
277        password_manager::switches::kEnableAutomaticPasswordSaving));
278  }
279
280  virtual void TearDownOnMainThread() OVERRIDE {
281    ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
282  }
283
284 protected:
285  content::WebContents* WebContents() {
286    return browser()->tab_strip_model()->GetActiveWebContents();
287  }
288
289  content::RenderViewHost* RenderViewHost() {
290    return WebContents()->GetRenderViewHost();
291  }
292
293  // Wrapper around ui_test_utils::NavigateToURL that waits until
294  // DidFinishLoad() fires. Normally this function returns after
295  // DidStopLoading(), which caused flakiness as the NavigationObserver
296  // would sometimes see the DidFinishLoad event from a previous navigation and
297  // return immediately.
298  void NavigateToFile(const std::string& path) {
299    NavigationObserver observer(WebContents());
300    GURL url = embedded_test_server()->GetURL(path);
301    ui_test_utils::NavigateToURL(browser(), url);
302    observer.Wait();
303  }
304
305  // Waits until the "value" attribute of the HTML element with |element_id| is
306  // equal to |expected_value|. If the current value is not as expected, this
307  // waits until the "change" event is fired for the element. This also
308  // guarantees that once the real value matches the expected, the JavaScript
309  // event loop is spun to allow all other possible events to take place.
310  void WaitForElementValue(const std::string& element_id,
311                           const std::string& expected_value);
312  // Checks that the current "value" attribute of the HTML element with
313  // |element_id| is equal to |expected_value|.
314  void CheckElementValue(const std::string& element_id,
315                         const std::string& expected_value);
316
317 private:
318  DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest);
319};
320
321void PasswordManagerBrowserTest::WaitForElementValue(
322    const std::string& element_id,
323    const std::string& expected_value) {
324  enum ReturnCodes {  // Possible results of the JavaScript code.
325    RETURN_CODE_OK,
326    RETURN_CODE_NO_ELEMENT,
327    RETURN_CODE_WRONG_VALUE,
328    RETURN_CODE_INVALID,
329  };
330  const std::string value_check_function = base::StringPrintf(
331      "function valueCheck() {"
332      "  var element = document.getElementById('%s');"
333      "  return element && element.value == '%s';"
334      "}",
335      element_id.c_str(),
336      expected_value.c_str());
337  const std::string script =
338      value_check_function +
339      base::StringPrintf(
340          "if (valueCheck()) {"
341          "  /* Spin the event loop with setTimeout. */"
342          "  setTimeout(window.domAutomationController.send(%d), 0);"
343          "} else {"
344          "  var element = document.getElementById('%s');"
345          "  if (!element)"
346          "    window.domAutomationController.send(%d);"
347          "  element.onchange = function() {"
348          "    if (valueCheck()) {"
349          "      /* Spin the event loop with setTimeout. */"
350          "      setTimeout(window.domAutomationController.send(%d), 0);"
351          "    } else {"
352          "      window.domAutomationController.send(%d);"
353          "    }"
354          "  };"
355          "}",
356          RETURN_CODE_OK,
357          element_id.c_str(),
358          RETURN_CODE_NO_ELEMENT,
359          RETURN_CODE_OK,
360          RETURN_CODE_WRONG_VALUE);
361  int return_value = RETURN_CODE_INVALID;
362  ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
363      RenderViewHost(), script, &return_value));
364  EXPECT_EQ(RETURN_CODE_OK, return_value)
365      << "element_id = " << element_id
366      << ", expected_value = " << expected_value;
367}
368
369void PasswordManagerBrowserTest::CheckElementValue(
370    const std::string& element_id,
371    const std::string& expected_value) {
372  const std::string value_check_script = base::StringPrintf(
373      "var element = document.getElementById('%s');"
374      "window.domAutomationController.send(element && element.value == '%s');",
375      element_id.c_str(),
376      expected_value.c_str());
377  bool return_value = false;
378  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
379      RenderViewHost(), value_check_script, &return_value));
380  EXPECT_TRUE(return_value) << "element_id = " << element_id
381                            << ", expected_value = " << expected_value;
382}
383
384// Actual tests ---------------------------------------------------------------
385IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
386                       PromptForNormalSubmit) {
387  NavigateToFile("/password/password_form.html");
388
389  // Fill a form and submit through a <input type="submit"> button. Nothing
390  // special.
391  NavigationObserver observer(WebContents());
392  scoped_ptr<PromptObserver> prompt_observer(
393      PromptObserver::Create(WebContents()));
394  std::string fill_and_submit =
395      "document.getElementById('username_field').value = 'temp';"
396      "document.getElementById('password_field').value = 'random';"
397      "document.getElementById('input_submit_button').click()";
398  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
399  observer.Wait();
400  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
401}
402
403IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
404                       PromptForSubmitWithInPageNavigation) {
405  NavigateToFile("/password/password_navigate_before_submit.html");
406
407  // Fill a form and submit through a <input type="submit"> button. Nothing
408  // special. The form does an in-page navigation before submitting.
409  NavigationObserver observer(WebContents());
410  scoped_ptr<PromptObserver> prompt_observer(
411      PromptObserver::Create(WebContents()));
412  std::string fill_and_submit =
413      "document.getElementById('username_field').value = 'temp';"
414      "document.getElementById('password_field').value = 'random';"
415      "document.getElementById('input_submit_button').click()";
416  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
417  observer.Wait();
418  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
419}
420
421IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
422                       LoginSuccessWithUnrelatedForm) {
423  // Log in, see a form on the landing page. That form is not related to the
424  // login form (=has a different action), so we should offer saving the
425  // password.
426  NavigateToFile("/password/password_form.html");
427
428  NavigationObserver observer(WebContents());
429  scoped_ptr<PromptObserver> prompt_observer(
430      PromptObserver::Create(WebContents()));
431  std::string fill_and_submit =
432      "document.getElementById('username_unrelated').value = 'temp';"
433      "document.getElementById('password_unrelated').value = 'random';"
434      "document.getElementById('submit_unrelated').click()";
435  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
436  observer.Wait();
437  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
438}
439
440IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, LoginFailed) {
441  // Log in, see a form on the landing page. That form is not related to the
442  // login form (=has a different action), so we should offer saving the
443  // password.
444  NavigateToFile("/password/password_form.html");
445
446  NavigationObserver observer(WebContents());
447  scoped_ptr<PromptObserver> prompt_observer(
448      PromptObserver::Create(WebContents()));
449  std::string fill_and_submit =
450      "document.getElementById('username_failed').value = 'temp';"
451      "document.getElementById('password_failed').value = 'random';"
452      "document.getElementById('submit_failed').click()";
453  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
454  observer.Wait();
455  EXPECT_FALSE(prompt_observer->IsShowingPrompt());
456}
457
458IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, Redirects) {
459  NavigateToFile("/password/password_form.html");
460
461  // Fill a form and submit through a <input type="submit"> button. The form
462  // points to a redirection page.
463  NavigationObserver observer(WebContents());
464  scoped_ptr<PromptObserver> prompt_observer(
465      PromptObserver::Create(WebContents()));
466  std::string fill_and_submit =
467      "document.getElementById('username_redirect').value = 'temp';"
468      "document.getElementById('password_redirect').value = 'random';"
469      "document.getElementById('submit_redirect').click()";
470  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
471  observer.Wait();
472  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
473
474  // The redirection page now redirects via Javascript. We check that the
475  // infobar stays.
476  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
477                                     "window.location.href = 'done.html';"));
478  observer.Wait();
479  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
480}
481
482IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
483                       PromptForSubmitUsingJavaScript) {
484  NavigateToFile("/password/password_form.html");
485
486  // Fill a form and submit using <button> that calls submit() on the form.
487  // This should work regardless of the type of element, as long as submit() is
488  // called.
489  NavigationObserver observer(WebContents());
490  scoped_ptr<PromptObserver> prompt_observer(
491      PromptObserver::Create(WebContents()));
492  std::string fill_and_submit =
493      "document.getElementById('username_field').value = 'temp';"
494      "document.getElementById('password_field').value = 'random';"
495      "document.getElementById('submit_button').click()";
496  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
497  observer.Wait();
498  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
499}
500
501// Flaky: crbug.com/301547, observed on win and mac. Probably happens on all
502// platforms.
503IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
504                       DISABLED_PromptForDynamicForm) {
505  NavigateToFile("/password/dynamic_password_form.html");
506
507  // Fill the dynamic password form and submit.
508  NavigationObserver observer(WebContents());
509  scoped_ptr<PromptObserver> prompt_observer(
510      PromptObserver::Create(WebContents()));
511  std::string fill_and_submit =
512      "document.getElementById('create_form_button').click();"
513      "window.setTimeout(function() {"
514      "  document.dynamic_form.username.value = 'tempro';"
515      "  document.dynamic_form.password.value = 'random';"
516      "  document.dynamic_form.submit();"
517      "}, 0)";
518  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
519  observer.Wait();
520  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
521}
522
523IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForNavigation) {
524  NavigateToFile("/password/password_form.html");
525
526  // Don't fill the password form, just navigate away. Shouldn't prompt.
527  NavigationObserver observer(WebContents());
528  scoped_ptr<PromptObserver> prompt_observer(
529      PromptObserver::Create(WebContents()));
530  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
531                                     "window.location.href = 'done.html';"));
532  observer.Wait();
533  EXPECT_FALSE(prompt_observer->IsShowingPrompt());
534}
535
536IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
537                       NoPromptForSubFrameNavigation) {
538  NavigateToFile("/password/multi_frames.html");
539
540  // If you are filling out a password form in one frame and a different frame
541  // navigates, this should not trigger the infobar.
542  NavigationObserver observer(WebContents());
543  scoped_ptr<PromptObserver> prompt_observer(
544      PromptObserver::Create(WebContents()));
545  observer.SetPathToWaitFor("/password/done.html");
546  std::string fill =
547      "var first_frame = document.getElementById('first_frame');"
548      "var frame_doc = first_frame.contentDocument;"
549      "frame_doc.getElementById('username_field').value = 'temp';"
550      "frame_doc.getElementById('password_field').value = 'random';";
551  std::string navigate_frame =
552      "var second_iframe = document.getElementById('second_frame');"
553      "second_iframe.contentWindow.location.href = 'done.html';";
554
555  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill));
556  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
557  observer.Wait();
558  EXPECT_FALSE(prompt_observer->IsShowingPrompt());
559}
560
561IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
562                       PromptAfterSubmitWithSubFrameNavigation) {
563  NavigateToFile("/password/multi_frames.html");
564
565  // Make sure that we prompt to save password even if a sub-frame navigation
566  // happens first.
567  NavigationObserver observer(WebContents());
568  scoped_ptr<PromptObserver> prompt_observer(
569      PromptObserver::Create(WebContents()));
570  observer.SetPathToWaitFor("/password/done.html");
571  std::string navigate_frame =
572      "var second_iframe = document.getElementById('second_frame');"
573      "second_iframe.contentWindow.location.href = 'other.html';";
574  std::string fill_and_submit =
575      "var first_frame = document.getElementById('first_frame');"
576      "var frame_doc = first_frame.contentDocument;"
577      "frame_doc.getElementById('username_field').value = 'temp';"
578      "frame_doc.getElementById('password_field').value = 'random';"
579      "frame_doc.getElementById('input_submit_button').click();";
580
581  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
582  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
583  observer.Wait();
584  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
585}
586
587IN_PROC_BROWSER_TEST_F(
588    PasswordManagerBrowserTest,
589    NoPromptForFailedLoginFromMainFrameWithMultiFramesInPage) {
590  NavigateToFile("/password/multi_frames.html");
591
592  // Make sure that we don't prompt to save the password for a failed login
593  // from the main frame with multiple frames in the same page.
594  NavigationObserver observer(WebContents());
595  scoped_ptr<PromptObserver> prompt_observer(
596      PromptObserver::Create(WebContents()));
597  std::string fill_and_submit =
598      "document.getElementById('username_failed').value = 'temp';"
599      "document.getElementById('password_failed').value = 'random';"
600      "document.getElementById('submit_failed').click();";
601
602  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
603  observer.Wait();
604  EXPECT_FALSE(prompt_observer->IsShowingPrompt());
605}
606
607IN_PROC_BROWSER_TEST_F(
608    PasswordManagerBrowserTest,
609    NoPromptForFailedLoginFromSubFrameWithMultiFramesInPage) {
610  NavigateToFile("/password/multi_frames.html");
611
612  // Make sure that we don't prompt to save the password for a failed login
613  // from a sub-frame with multiple frames in the same page.
614  NavigationObserver observer(WebContents());
615  scoped_ptr<PromptObserver> prompt_observer(
616      PromptObserver::Create(WebContents()));
617  std::string fill_and_submit =
618      "var first_frame = document.getElementById('first_frame');"
619      "var frame_doc = first_frame.contentDocument;"
620      "frame_doc.getElementById('username_failed').value = 'temp';"
621      "frame_doc.getElementById('password_failed').value = 'random';"
622      "frame_doc.getElementById('submit_failed').click();"
623      "window.parent.location.href = 'multi_frames.html';";
624
625  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
626  observer.Wait();
627  EXPECT_FALSE(prompt_observer->IsShowingPrompt());
628}
629
630IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForXHRSubmit) {
631#if defined(OS_WIN) && defined(USE_ASH)
632  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
633  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
634    return;
635#endif
636  NavigateToFile("/password/password_xhr_submit.html");
637
638  // Verify that we show the save password prompt if a form returns false
639  // in its onsubmit handler but instead logs in/navigates via XHR.
640  // Note that calling 'submit()' on a form with javascript doesn't call
641  // the onsubmit handler, so we click the submit button instead.
642  NavigationObserver observer(WebContents());
643  scoped_ptr<PromptObserver> prompt_observer(
644      PromptObserver::Create(WebContents()));
645  std::string fill_and_submit =
646      "document.getElementById('username_field').value = 'temp';"
647      "document.getElementById('password_field').value = 'random';"
648      "document.getElementById('submit_button').click()";
649  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
650  observer.Wait();
651  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
652}
653
654IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
655                       PromptForXHRWithoutOnSubmit) {
656  NavigateToFile("/password/password_xhr_submit.html");
657
658  // Verify that if XHR navigation occurs and the form is properly filled out,
659  // we try and save the password even though onsubmit hasn't been called.
660  NavigationObserver observer(WebContents());
661  scoped_ptr<PromptObserver> prompt_observer(
662      PromptObserver::Create(WebContents()));
663  std::string fill_and_navigate =
664      "document.getElementById('username_field').value = 'temp';"
665      "document.getElementById('password_field').value = 'random';"
666      "send_xhr()";
667  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate));
668  observer.Wait();
669  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
670}
671
672IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptIfLinkClicked) {
673  NavigateToFile("/password/password_form.html");
674
675  // Verify that if the user takes a direct action to leave the page, we don't
676  // prompt to save the password even if the form is already filled out.
677  NavigationObserver observer(WebContents());
678  scoped_ptr<PromptObserver> prompt_observer(
679      PromptObserver::Create(WebContents()));
680  std::string fill_and_click_link =
681      "document.getElementById('username_field').value = 'temp';"
682      "document.getElementById('password_field').value = 'random';"
683      "document.getElementById('link').click();";
684  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_click_link));
685  observer.Wait();
686  EXPECT_FALSE(prompt_observer->IsShowingPrompt());
687}
688
689// TODO(jam): http://crbug.com/350550
690#if !defined(OS_WIN)
691IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
692                       VerifyPasswordGenerationUpload) {
693  // Prevent Autofill requests from actually going over the wire.
694  net::TestURLFetcherFactory factory;
695  // Disable Autofill requesting access to AddressBook data. This causes
696  // the test to hang on Mac.
697  autofill::test::DisableSystemServices(browser()->profile()->GetPrefs());
698
699  // Visit a signup form.
700  NavigateToFile("/password/signup_form.html");
701
702  // Enter a password and save it.
703  NavigationObserver first_observer(WebContents());
704  scoped_ptr<PromptObserver> prompt_observer(
705      PromptObserver::Create(WebContents()));
706  std::string fill_and_submit =
707      "document.getElementById('other_info').value = 'stuff';"
708      "document.getElementById('username_field').value = 'my_username';"
709      "document.getElementById('password_field').value = 'password';"
710      "document.getElementById('input_submit_button').click()";
711  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
712
713  first_observer.Wait();
714  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
715  prompt_observer->Accept();
716
717  // Now navigate to a login form that has similar HTML markup.
718  NavigateToFile("/password/password_form.html");
719
720  // Simulate a user click to force an autofill of the form's DOM value, not
721  // just the suggested value.
722  content::SimulateMouseClick(
723      WebContents(), 0, blink::WebMouseEvent::ButtonLeft);
724
725  // The form should be filled with the previously submitted username.
726  std::string get_username =
727      "window.domAutomationController.send("
728      "document.getElementById('username_field').value);";
729  std::string actual_username;
730  ASSERT_TRUE(content::ExecuteScriptAndExtractString(RenderViewHost(),
731                                                     get_username,
732                                                     &actual_username));
733  ASSERT_EQ("my_username", actual_username);
734
735  // Submit the form and verify that there is no infobar (as the password
736  // has already been saved).
737  NavigationObserver second_observer(WebContents());
738  scoped_ptr<PromptObserver> second_prompt_observer(
739      PromptObserver::Create(WebContents()));
740  std::string submit_form =
741      "document.getElementById('input_submit_button').click()";
742  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit_form));
743  second_observer.Wait();
744  EXPECT_FALSE(second_prompt_observer->IsShowingPrompt());
745
746  // Verify that we sent two pings to Autofill. One vote for of PASSWORD for
747  // the current form, and one vote for ACCOUNT_CREATION_PASSWORD on the
748  // original form since it has more than 2 text input fields and was used for
749  // the first time on a different form.
750  base::HistogramBase* upload_histogram =
751      base::StatisticsRecorder::FindHistogram(
752          "PasswordGeneration.UploadStarted");
753  ASSERT_TRUE(upload_histogram);
754  scoped_ptr<base::HistogramSamples> snapshot =
755      upload_histogram->SnapshotSamples();
756  EXPECT_EQ(0, snapshot->GetCount(0 /* failure */));
757  EXPECT_EQ(2, snapshot->GetCount(1 /* success */));
758}
759#endif
760
761IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForSubmitFromIframe) {
762  NavigateToFile("/password/password_submit_from_iframe.html");
763
764  // Submit a form in an iframe, then cause the whole page to navigate without a
765  // user gesture. We expect the save password prompt to be shown here, because
766  // some pages use such iframes for login forms.
767  NavigationObserver observer(WebContents());
768  scoped_ptr<PromptObserver> prompt_observer(
769      PromptObserver::Create(WebContents()));
770  std::string fill_and_submit =
771      "var iframe = document.getElementById('test_iframe');"
772      "var iframe_doc = iframe.contentDocument;"
773      "iframe_doc.getElementById('username_field').value = 'temp';"
774      "iframe_doc.getElementById('password_field').value = 'random';"
775      "iframe_doc.getElementById('submit_button').click()";
776
777  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
778  observer.Wait();
779  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
780}
781
782IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
783                       PromptForInputElementWithoutName) {
784  // Check that the prompt is shown for forms where input elements lack the
785  // "name" attribute but the "id" is present.
786  NavigateToFile("/password/password_form.html");
787
788  NavigationObserver observer(WebContents());
789  scoped_ptr<PromptObserver> prompt_observer(
790      PromptObserver::Create(WebContents()));
791  std::string fill_and_submit =
792      "document.getElementById('username_field_no_name').value = 'temp';"
793      "document.getElementById('password_field_no_name').value = 'random';"
794      "document.getElementById('input_submit_button_no_name').click()";
795  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
796  observer.Wait();
797  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
798}
799
800IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
801                       PromptForInputElementWithoutId) {
802  // Check that the prompt is shown for forms where input elements lack the
803  // "id" attribute but the "name" attribute is present.
804  NavigateToFile("/password/password_form.html");
805
806  NavigationObserver observer(WebContents());
807  scoped_ptr<PromptObserver> prompt_observer(
808      PromptObserver::Create(WebContents()));
809  std::string fill_and_submit =
810      "document.getElementsByName('username_field_no_id')[0].value = 'temp';"
811      "document.getElementsByName('password_field_no_id')[0].value = 'random';"
812      "document.getElementsByName('input_submit_button_no_id')[0].click()";
813  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
814  observer.Wait();
815  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
816}
817
818IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
819                       NoPromptForInputElementWithoutIdAndName) {
820  // Check that no prompt is shown for forms where the input fields lack both
821  // the "id" and the "name" attributes.
822  NavigateToFile("/password/password_form.html");
823
824  NavigationObserver observer(WebContents());
825  scoped_ptr<PromptObserver> prompt_observer(
826      PromptObserver::Create(WebContents()));
827  std::string fill_and_submit =
828      "var form = document.getElementById('testform_elements_no_id_no_name');"
829      "var username = form.children[0];"
830      "username.value = 'temp';"
831      "var password = form.children[1];"
832      "password.value = 'random';"
833      "form.children[2].click()";  // form.children[2] is the submit button.
834  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
835  observer.Wait();
836  EXPECT_FALSE(prompt_observer->IsShowingPrompt());
837}
838
839// Test for checking that no prompt is shown for URLs with file: scheme.
840IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
841                       NoPromptForFileSchemeURLs) {
842  GURL url = GetFileURL("password_form.html");
843  ui_test_utils::NavigateToURL(browser(), url);
844
845  NavigationObserver observer(WebContents());
846  scoped_ptr<PromptObserver> prompt_observer(
847      PromptObserver::Create(WebContents()));
848  std::string fill_and_submit =
849      "document.getElementById('username_field').value = 'temp';"
850      "document.getElementById('password_field').value = 'random';"
851      "document.getElementById('input_submit_button').click();";
852  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
853  observer.Wait();
854  EXPECT_FALSE(prompt_observer->IsShowingPrompt());
855}
856
857IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, DeleteFrameBeforeSubmit) {
858  NavigateToFile("/password/multi_frames.html");
859
860  NavigationObserver observer(WebContents());
861  // Make sure we save some password info from an iframe and then destroy it.
862  std::string save_and_remove =
863      "var first_frame = document.getElementById('first_frame');"
864      "var frame_doc = first_frame.contentDocument;"
865      "frame_doc.getElementById('username_field').value = 'temp';"
866      "frame_doc.getElementById('password_field').value = 'random';"
867      "frame_doc.getElementById('input_submit_button').click();"
868      "first_frame.parentNode.removeChild(first_frame);";
869  // Submit from the main frame, but without navigating through the onsubmit
870  // handler.
871  std::string navigate_frame =
872      "document.getElementById('username_field').value = 'temp';"
873      "document.getElementById('password_field').value = 'random';"
874      "document.getElementById('input_submit_button').click();"
875      "window.location.href = 'done.html';";
876
877  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), save_and_remove));
878  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
879  observer.Wait();
880  // The only thing we check here is that there is no use-after-free reported.
881}
882
883IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PasswordValueAccessible) {
884  NavigateToFile("/password/form_and_link.html");
885
886  // Click on a link to open a new tab, then switch back to the first one.
887  EXPECT_EQ(1, browser()->tab_strip_model()->count());
888  std::string click =
889      "document.getElementById('testlink').click();";
890  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), click));
891  EXPECT_EQ(2, browser()->tab_strip_model()->count());
892  browser()->tab_strip_model()->ActivateTabAt(0, false);
893
894  // Fill in the credentials, and make sure they are saved.
895  NavigationObserver form_submit_observer(WebContents());
896  scoped_ptr<PromptObserver> prompt_observer(
897      PromptObserver::Create(WebContents()));
898  std::string fill_and_submit =
899      "document.getElementById('username_field').value = 'temp';"
900      "document.getElementById('password_field').value = 'random';"
901      "document.getElementById('input_submit_button').click();";
902  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
903  form_submit_observer.Wait();
904  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
905  prompt_observer->Accept();
906
907  // Reload the original page to have the saved credentials autofilled.
908  NavigationObserver reload_observer(WebContents());
909  NavigateToFile("/password/form_and_link.html");
910  reload_observer.Wait();
911
912  // Wait until the username is filled, to make sure autofill kicked in.
913  WaitForElementValue("username_field", "temp");
914  // Now check that the password is not accessible yet.
915  CheckElementValue("password_field", "");
916  // Let the user interact with the page.
917  content::SimulateMouseClickAt(
918      WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(1, 1));
919  // Wait until that interaction causes the password value to be revealed.
920  WaitForElementValue("password_field", "random");
921  // And check that after the side-effects of the interaction took place, the
922  // username value stays the same.
923  CheckElementValue("username_field", "temp");
924}
925
926// The following test is limited to Aura, because
927// RenderWidgetHostViewGuest::ProcessAckedTouchEvent is, and
928// ProcessAckedTouchEvent is what triggers the translation of touch events to
929// gesture events.
930#if defined(USE_AURA)
931IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
932                       PasswordValueAccessibleOnSubmit) {
933  NavigateToFile("/password/form_and_link.html");
934
935  // Fill in the credentials, and make sure they are saved.
936  NavigationObserver form_submit_observer(WebContents());
937  scoped_ptr<PromptObserver> prompt_observer(
938      PromptObserver::Create(WebContents()));
939  std::string fill_and_submit =
940      "document.getElementById('username_field').value = 'temp';"
941      "document.getElementById('password_field').value = 'random_secret';"
942      "document.getElementById('input_submit_button').click();";
943  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
944  form_submit_observer.Wait();
945  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
946  prompt_observer->Accept();
947
948  // Reload the original page to have the saved credentials autofilled.
949  NavigationObserver reload_observer(WebContents());
950  NavigateToFile("/password/form_and_link.html");
951  reload_observer.Wait();
952
953  NavigationObserver submit_observer(WebContents());
954  // Submit the form via a tap on the submit button. The button is placed at 0,
955  // 100, and has height 300 and width 700.
956  content::SimulateTapAt(WebContents(), gfx::Point(350, 250));
957  submit_observer.Wait();
958  std::string query = WebContents()->GetURL().query();
959  EXPECT_NE(std::string::npos, query.find("random_secret")) << query;
960}
961#endif
962
963// Test fix for crbug.com/338650.
964IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
965                       DontPromptForPasswordFormWithDefaultValue) {
966  NavigateToFile("/password/password_form_with_default_value.html");
967
968  // Don't prompt if we navigate away even if there is a password value since
969  // it's not coming from the user.
970  NavigationObserver observer(WebContents());
971  scoped_ptr<PromptObserver> prompt_observer(
972      PromptObserver::Create(WebContents()));
973  NavigateToFile("/password/done.html");
974  observer.Wait();
975  EXPECT_FALSE(prompt_observer->IsShowingPrompt());
976}
977
978IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
979                       PromptWhenEnableAutomaticPasswordSavingSwitchIsNotSet) {
980  NavigateToFile("/password/password_form.html");
981
982  // Fill a form and submit through a <input type="submit"> button.
983  NavigationObserver observer(WebContents());
984  scoped_ptr<PromptObserver> prompt_observer(
985      PromptObserver::Create(WebContents()));
986  std::string fill_and_submit =
987      "document.getElementById('username_field').value = 'temp';"
988      "document.getElementById('password_field').value = 'random';"
989      "document.getElementById('input_submit_button').click()";
990  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
991  observer.Wait();
992  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
993}
994
995IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
996                       DontPromptWhenEnableAutomaticPasswordSavingSwitchIsSet) {
997  password_manager::TestPasswordStore* password_store =
998      static_cast<password_manager::TestPasswordStore*>(
999          PasswordStoreFactory::GetForProfile(browser()->profile(),
1000                                              Profile::IMPLICIT_ACCESS).get());
1001
1002  EXPECT_TRUE(password_store->IsEmpty());
1003
1004  NavigateToFile("/password/password_form.html");
1005
1006  // Add the enable-automatic-password-saving switch.
1007  CommandLine::ForCurrentProcess()->AppendSwitch(
1008      password_manager::switches::kEnableAutomaticPasswordSaving);
1009
1010  // Fill a form and submit through a <input type="submit"> button.
1011  NavigationObserver observer(WebContents());
1012  scoped_ptr<PromptObserver> prompt_observer(
1013      PromptObserver::Create(WebContents()));
1014  // Make sure that the only passwords saved are the auto-saved ones.
1015  std::string fill_and_submit =
1016      "document.getElementById('username_field').value = 'temp';"
1017      "document.getElementById('password_field').value = 'random';"
1018      "document.getElementById('input_submit_button').click()";
1019  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
1020  observer.Wait();
1021  if (chrome::VersionInfo::GetChannel() ==
1022      chrome::VersionInfo::CHANNEL_UNKNOWN) {
1023    // Passwords getting auto-saved, no prompt.
1024    EXPECT_FALSE(prompt_observer->IsShowingPrompt());
1025    EXPECT_FALSE(password_store->IsEmpty());
1026  } else {
1027    // Prompt shown, and no passwords saved automatically.
1028    EXPECT_TRUE(prompt_observer->IsShowingPrompt());
1029    EXPECT_TRUE(password_store->IsEmpty());
1030  }
1031}
1032
1033// Test fix for crbug.com/368690.
1034IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptWhenReloading) {
1035  NavigateToFile("/password/password_form.html");
1036
1037  std::string fill =
1038      "document.getElementById('username_redirect').value = 'temp';"
1039      "document.getElementById('password_redirect').value = 'random';";
1040  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill));
1041
1042  NavigationObserver observer(WebContents());
1043  scoped_ptr<PromptObserver> prompt_observer(
1044      PromptObserver::Create(WebContents()));
1045  GURL url = embedded_test_server()->GetURL("/password/password_form.html");
1046  chrome::NavigateParams params(browser(), url,
1047                                ui::PAGE_TRANSITION_RELOAD);
1048  ui_test_utils::NavigateToURL(&params);
1049  observer.Wait();
1050  EXPECT_FALSE(prompt_observer->IsShowingPrompt());
1051}
1052
1053// Test that if a form gets dynamically added between the form parsing and
1054// rendering, and while the main frame still loads, it still is registered, and
1055// thus saving passwords from it works.
1056IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
1057                       FormsAddedBetweenParsingAndRendering) {
1058  NavigateToFile("/password/between_parsing_and_rendering.html");
1059
1060  NavigationObserver observer(WebContents());
1061  scoped_ptr<PromptObserver> prompt_observer(
1062      PromptObserver::Create(WebContents()));
1063  std::string submit =
1064      "document.getElementById('username').value = 'temp';"
1065      "document.getElementById('password').value = 'random';"
1066      "document.getElementById('submit-button').click();";
1067  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit));
1068  observer.Wait();
1069
1070  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
1071}
1072
1073// Test that if there was no previous page load then the PasswordManagerDriver
1074// does not think that there were SSL errors on the current page. The test opens
1075// a new tab with a URL for which the embedded test server issues a basic auth
1076// challenge.
1077IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoLastLoadGoodLastLoad) {
1078  // Teach the embedded server to handle requests by issuing the basic auth
1079  // challenge.
1080  embedded_test_server()->RegisterRequestHandler(
1081      base::Bind(&HandleTestAuthRequest));
1082
1083  LoginPromptBrowserTestObserver login_observer;
1084  // We need to register to all sources, because the navigation observer we are
1085  // interested in is for a new tab to be opened, and thus does not exist yet.
1086  login_observer.Register(content::NotificationService::AllSources());
1087
1088  password_manager::TestPasswordStore* password_store =
1089      static_cast<password_manager::TestPasswordStore*>(
1090          PasswordStoreFactory::GetForProfile(browser()->profile(),
1091                                              Profile::IMPLICIT_ACCESS).get());
1092  EXPECT_TRUE(password_store->IsEmpty());
1093
1094  // Navigate to a page requiring HTTP auth. Wait for the tab to get the correct
1095  // WebContents, but don't wait for navigation, which only finishes after
1096  // authentication.
1097  ui_test_utils::NavigateToURLWithDisposition(
1098      browser(),
1099      embedded_test_server()->GetURL("/basic_auth"),
1100      NEW_FOREGROUND_TAB,
1101      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
1102
1103  content::NavigationController* nav_controller =
1104      &WebContents()->GetController();
1105  NavigationObserver nav_observer(WebContents());
1106  scoped_ptr<PromptObserver> prompt_observer(
1107      PromptObserver::Create(WebContents()));
1108  WindowedAuthNeededObserver auth_needed_observer(nav_controller);
1109  auth_needed_observer.Wait();
1110
1111  WindowedAuthSuppliedObserver auth_supplied_observer(nav_controller);
1112  // Offer valid credentials on the auth challenge.
1113  ASSERT_EQ(1u, login_observer.handlers().size());
1114  LoginHandler* handler = *login_observer.handlers().begin();
1115  ASSERT_TRUE(handler);
1116  // Any username/password will work.
1117  handler->SetAuth(base::UTF8ToUTF16("user"), base::UTF8ToUTF16("pwd"));
1118  auth_supplied_observer.Wait();
1119
1120  // The password manager should be working correctly.
1121  nav_observer.Wait();
1122  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
1123  prompt_observer->Accept();
1124
1125  // Spin the message loop to make sure the password store had a chance to save
1126  // the password.
1127  base::RunLoop run_loop;
1128  run_loop.RunUntilIdle();
1129  EXPECT_FALSE(password_store->IsEmpty());
1130}
1131
1132// In some situations, multiple PasswordFormManager instances from
1133// PasswordManager::pending_login_managers_ would match (via DoesManage) a form
1134// to be provisionally saved. One of them might be a complete match, the other
1135// all-but-action match. Normally, the former should be preferred, but if the
1136// former has not finished matching, and the latter has, the latter should be
1137// used (otherwise we'd give up even though we could have saved the password).
1138IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
1139                       PreferPasswordFormManagerWhichFinishedMatching) {
1140  NavigateToFile("/password/create_form_copy_on_submit.html");
1141
1142  NavigationObserver observer(WebContents());
1143  scoped_ptr<PromptObserver> prompt_observer(
1144      PromptObserver::Create(WebContents()));
1145  std::string submit =
1146      "document.getElementById('username').value = 'overwrite_me';"
1147      "document.getElementById('password').value = 'random';"
1148      "document.getElementById('non-form-button').click();";
1149  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit));
1150  observer.Wait();
1151
1152  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
1153}
1154
1155// Test that if login fails and content server pushes a different login form
1156// with action URL having different schemes. Heuristic shall be able
1157// identify such cases and *shall not* prompt to save incorrect password.
1158IN_PROC_BROWSER_TEST_F(
1159    PasswordManagerBrowserTest,
1160    NoPromptForLoginFailedAndServerPushSeperateLoginForm_HttpToHttps) {
1161  std::string path =
1162      "/password/separate_login_form_with_onload_submit_script.html";
1163  GURL http_url(embedded_test_server()->GetURL(path));
1164  ASSERT_TRUE(http_url.SchemeIs(url::kHttpScheme));
1165
1166  NavigationObserver observer(WebContents());
1167  scoped_ptr<PromptObserver> prompt_observer(
1168      PromptObserver::Create(WebContents()));
1169  ui_test_utils::NavigateToURL(browser(), http_url);
1170
1171  observer.SetPathToWaitFor("/password/done_and_separate_login_form.html");
1172  observer.Wait();
1173
1174  EXPECT_FALSE(prompt_observer->IsShowingPrompt());
1175}
1176
1177IN_PROC_BROWSER_TEST_F(
1178    PasswordManagerBrowserTest,
1179    NoPromptForLoginFailedAndServerPushSeperateLoginForm_HttpsToHttp) {
1180  CommandLine::ForCurrentProcess()->AppendSwitch(
1181      switches::kAllowRunningInsecureContent);
1182  CommandLine::ForCurrentProcess()->AppendSwitch(
1183      switches::kIgnoreCertificateErrors);
1184  const base::FilePath::CharType kDocRoot[] =
1185      FILE_PATH_LITERAL("chrome/test/data");
1186  net::SpawnedTestServer https_test_server(
1187      net::SpawnedTestServer::TYPE_HTTPS,
1188      net::SpawnedTestServer::SSLOptions(
1189          net::SpawnedTestServer::SSLOptions::CERT_OK),
1190      base::FilePath(kDocRoot));
1191  ASSERT_TRUE(https_test_server.Start());
1192
1193  // This test case cannot inject the scripts via content::ExecuteScript() in
1194  // files served through HTTPS. Therefore the scripts are made part of the HTML
1195  // site and executed on load.
1196  std::string path =
1197      "password/separate_login_form_with_onload_submit_script.html";
1198  GURL https_url(https_test_server.GetURL(path));
1199  ASSERT_TRUE(https_url.SchemeIs(url::kHttpsScheme));
1200
1201  NavigationObserver observer(WebContents());
1202  scoped_ptr<PromptObserver> prompt_observer(
1203      PromptObserver::Create(WebContents()));
1204  ui_test_utils::NavigateToURL(browser(), https_url);
1205
1206  observer.SetPathToWaitFor("/password/done_and_separate_login_form.html");
1207  observer.Wait();
1208
1209  EXPECT_FALSE(prompt_observer->IsShowingPrompt());
1210}
1211
1212IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
1213                       PromptWhenPasswordFormWithoutUsernameFieldSubmitted) {
1214  password_manager::TestPasswordStore* password_store =
1215      static_cast<password_manager::TestPasswordStore*>(
1216          PasswordStoreFactory::GetForProfile(browser()->profile(),
1217                                              Profile::IMPLICIT_ACCESS).get());
1218
1219  EXPECT_TRUE(password_store->IsEmpty());
1220
1221  NavigateToFile("/password/form_with_only_password_field.html");
1222
1223  NavigationObserver observer(WebContents());
1224  scoped_ptr<PromptObserver> prompt_observer(
1225      PromptObserver::Create(WebContents()));
1226  std::string submit =
1227      "document.getElementById('password').value = 'password';"
1228      "document.getElementById('submit-button').click();";
1229  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit));
1230  observer.Wait();
1231
1232  EXPECT_TRUE(prompt_observer->IsShowingPrompt());
1233  prompt_observer->Accept();
1234
1235  // Spin the message loop to make sure the password store had a chance to save
1236  // the password.
1237  base::RunLoop run_loop;
1238  run_loop.RunUntilIdle();
1239  EXPECT_FALSE(password_store->IsEmpty());
1240}
1241
1242IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
1243                       AutofillSuggetionsForPasswordFormWithoutUsernameField) {
1244  password_manager::TestPasswordStore* password_store =
1245      static_cast<password_manager::TestPasswordStore*>(
1246          PasswordStoreFactory::GetForProfile(browser()->profile(),
1247                                              Profile::IMPLICIT_ACCESS).get());
1248
1249  EXPECT_TRUE(password_store->IsEmpty());
1250
1251  // Password form without username-field.
1252  NavigateToFile("/password/form_with_only_password_field.html");
1253
1254  NavigationObserver observer(WebContents());
1255  scoped_ptr<PromptObserver> prompt_observer(
1256      PromptObserver::Create(WebContents()));
1257  std::string submit =
1258      "document.getElementById('password').value = 'mypassword';"
1259      "document.getElementById('submit-button').click();";
1260  ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit));
1261  observer.Wait();
1262
1263  prompt_observer->Accept();
1264
1265  // Spin the message loop to make sure the password store had a chance to save
1266  // the password.
1267  base::RunLoop run_loop;
1268  run_loop.RunUntilIdle();
1269  EXPECT_FALSE(password_store->IsEmpty());
1270
1271  // Now, navigate to same html password form and verify whether password is
1272  // autofilled.
1273  NavigateToFile("/password/form_with_only_password_field.html");
1274
1275  // Let the user interact with the page, so that DOM gets modification events,
1276  // needed for autofilling fields.
1277  content::SimulateMouseClickAt(
1278      WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(1, 1));
1279
1280  // Wait until that interaction causes the password value to be revealed.
1281  WaitForElementValue("password", "mypassword");
1282}
1283