• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/strings/stringprintf.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/infobars/infobar_service.h"
13 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
14 #include "chrome/browser/password_manager/password_store_factory.h"
15 #include "chrome/browser/password_manager/test_password_store_service.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
19 #include "chrome/common/chrome_version_info.h"
20 #include "chrome/test/base/in_process_browser_test.h"
21 #include "chrome/test/base/test_switches.h"
22 #include "chrome/test/base/ui_test_utils.h"
23 #include "components/autofill/core/browser/autofill_test_utils.h"
24 #include "components/infobars/core/confirm_infobar_delegate.h"
25 #include "components/infobars/core/infobar.h"
26 #include "components/infobars/core/infobar_manager.h"
27 #include "components/password_manager/core/browser/test_password_store.h"
28 #include "components/password_manager/core/common/password_manager_switches.h"
29 #include "content/public/browser/render_view_host.h"
30 #include "content/public/browser/web_contents.h"
31 #include "content/public/browser/web_contents_observer.h"
32 #include "content/public/test/browser_test_utils.h"
33 #include "content/public/test/test_utils.h"
34 #include "net/test/embedded_test_server/embedded_test_server.h"
35 #include "net/url_request/test_url_fetcher_factory.h"
36 #include "testing/gmock/include/gmock/gmock.h"
37 #include "third_party/WebKit/public/web/WebInputEvent.h"
38 #include "ui/events/keycodes/keyboard_codes.h"
39 #include "ui/gfx/geometry/point.h"
40 
41 
42 // NavigationObserver ---------------------------------------------------------
43 
44 namespace {
45 
46 // Observer that waits for navigation to complete and for the password infobar
47 // to be shown.
48 class NavigationObserver : public content::WebContentsObserver,
49                            public infobars::InfoBarManager::Observer {
50  public:
NavigationObserver(content::WebContents * web_contents)51   explicit NavigationObserver(content::WebContents* web_contents)
52       : content::WebContentsObserver(web_contents),
53         message_loop_runner_(new content::MessageLoopRunner),
54         infobar_shown_(false),
55         infobar_removed_(false),
56         should_automatically_accept_infobar_(true),
57         infobar_service_(InfoBarService::FromWebContents(web_contents)) {
58     infobar_service_->AddObserver(this);
59   }
60 
~NavigationObserver()61   virtual ~NavigationObserver() {
62     if (infobar_service_)
63       infobar_service_->RemoveObserver(this);
64   }
65 
66   // Normally Wait() will not return until a main frame navigation occurs.
67   // If a path is set, Wait() will return after this path has been seen,
68   // regardless of the frame that navigated. Useful for multi-frame pages.
SetPathToWaitFor(const std::string & path)69   void SetPathToWaitFor(const std::string& path) {
70     wait_for_path_ = path;
71   }
72 
73   // content::WebContentsObserver:
DidFinishLoad(int64 frame_id,const GURL & validated_url,bool is_main_frame,content::RenderViewHost * render_view_host)74   virtual void DidFinishLoad(
75       int64 frame_id,
76       const GURL& validated_url,
77       bool is_main_frame,
78       content::RenderViewHost* render_view_host) OVERRIDE {
79     if (!wait_for_path_.empty()) {
80       if (validated_url.path() == wait_for_path_)
81         message_loop_runner_->Quit();
82     } else if (is_main_frame) {
83       message_loop_runner_->Quit();
84     }
85   }
86 
infobar_shown() const87   bool infobar_shown() const { return infobar_shown_; }
infobar_removed() const88   bool infobar_removed() const { return infobar_removed_; }
89 
disable_should_automatically_accept_infobar()90   void disable_should_automatically_accept_infobar() {
91     should_automatically_accept_infobar_ = false;
92   }
93 
Wait()94   void Wait() {
95     message_loop_runner_->Run();
96   }
97 
98  private:
99   // infobars::InfoBarManager::Observer:
OnInfoBarAdded(infobars::InfoBar * infobar)100   virtual void OnInfoBarAdded(infobars::InfoBar* infobar) OVERRIDE {
101     if (should_automatically_accept_infobar_) {
102       infobar_service_->infobar_at(0)->delegate()->
103           AsConfirmInfoBarDelegate()->Accept();
104     }
105     infobar_shown_ = true;
106   }
107 
OnInfoBarRemoved(infobars::InfoBar * infobar,bool animate)108   virtual void OnInfoBarRemoved(infobars::InfoBar* infobar,
109                                 bool animate) OVERRIDE {
110     infobar_removed_ = true;
111   }
112 
OnManagerShuttingDown(infobars::InfoBarManager * manager)113   virtual void OnManagerShuttingDown(
114       infobars::InfoBarManager* manager) OVERRIDE {
115     ASSERT_EQ(infobar_service_, manager);
116     infobar_service_->RemoveObserver(this);
117     infobar_service_ = NULL;
118   }
119 
120   std::string wait_for_path_;
121   scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
122   bool infobar_shown_;
123   bool infobar_removed_;
124   // If |should_automatically_accept_infobar_| is true, then whenever the test
125   // sees an infobar added, it will click its accepting button. Default = true.
126   bool should_automatically_accept_infobar_;
127   InfoBarService* infobar_service_;
128 
129   DISALLOW_COPY_AND_ASSIGN(NavigationObserver);
130 };
131 
132 }  // namespace
133 
134 
135 // PasswordManagerBrowserTest -------------------------------------------------
136 
137 class PasswordManagerBrowserTest : public InProcessBrowserTest {
138  public:
PasswordManagerBrowserTest()139   PasswordManagerBrowserTest() {}
~PasswordManagerBrowserTest()140   virtual ~PasswordManagerBrowserTest() {}
141 
142   // InProcessBrowserTest:
SetUpOnMainThread()143   virtual void SetUpOnMainThread() OVERRIDE {
144     // Use TestPasswordStore to remove a possible race. Normally the
145     // PasswordStore does its database manipulation on the DB thread, which
146     // creates a possible race during navigation. Specifically the
147     // PasswordManager will ignore any forms in a page if the load from the
148     // PasswordStore has not completed.
149     PasswordStoreFactory::GetInstance()->SetTestingFactory(
150         browser()->profile(), TestPasswordStoreService::Build);
151   }
152 
153  protected:
WebContents()154   content::WebContents* WebContents() {
155     return browser()->tab_strip_model()->GetActiveWebContents();
156   }
157 
RenderViewHost()158   content::RenderViewHost* RenderViewHost() {
159     return WebContents()->GetRenderViewHost();
160   }
161 
controller()162   ManagePasswordsUIController* controller() {
163     return ManagePasswordsUIController::FromWebContents(WebContents());
164   }
165 
166   // Wrapper around ui_test_utils::NavigateToURL that waits until
167   // DidFinishLoad() fires. Normally this function returns after
168   // DidStopLoading(), which caused flakiness as the NavigationObserver
169   // would sometimes see the DidFinishLoad event from a previous navigation and
170   // return immediately.
NavigateToFile(const std::string & path)171   void NavigateToFile(const std::string& path) {
172     if (!embedded_test_server()->Started())
173       ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
174 
175     ASSERT_FALSE(CommandLine::ForCurrentProcess()->HasSwitch(
176         password_manager::switches::kEnableAutomaticPasswordSaving));
177     NavigationObserver observer(WebContents());
178     GURL url = embedded_test_server()->GetURL(path);
179     ui_test_utils::NavigateToURL(browser(), url);
180     observer.Wait();
181   }
182 
183   // Waits until the "value" attribute of the HTML element with |element_id| is
184   // equal to |expected_value|. If the current value is not as expected, this
185   // waits until the "change" event is fired for the element. This also
186   // guarantees that once the real value matches the expected, the JavaScript
187   // event loop is spun to allow all other possible events to take place.
188   void WaitForElementValue(const std::string& element_id,
189                            const std::string& expected_value);
190   // Checks that the current "value" attribute of the HTML element with
191   // |element_id| is equal to |expected_value|.
192   void CheckElementValue(const std::string& element_id,
193                          const std::string& expected_value);
194 
195  private:
196   DISALLOW_COPY_AND_ASSIGN(PasswordManagerBrowserTest);
197 };
198 
WaitForElementValue(const std::string & element_id,const std::string & expected_value)199 void PasswordManagerBrowserTest::WaitForElementValue(
200     const std::string& element_id,
201     const std::string& expected_value) {
202   enum ReturnCodes {  // Possible results of the JavaScript code.
203     RETURN_CODE_OK,
204     RETURN_CODE_NO_ELEMENT,
205     RETURN_CODE_WRONG_VALUE,
206     RETURN_CODE_INVALID,
207   };
208   const std::string value_check_function = base::StringPrintf(
209       "function valueCheck() {"
210       "  var element = document.getElementById('%s');"
211       "  return element && element.value == '%s';"
212       "}",
213       element_id.c_str(),
214       expected_value.c_str());
215   const std::string script =
216       value_check_function +
217       base::StringPrintf(
218           "if (valueCheck()) {"
219           "  /* Spin the event loop with setTimeout. */"
220           "  setTimeout(window.domAutomationController.send(%d), 0);"
221           "} else {"
222           "  var element = document.getElementById('%s');"
223           "  if (!element)"
224           "    window.domAutomationController.send(%d);"
225           "  element.onchange = function() {"
226           "    if (valueCheck()) {"
227           "      /* Spin the event loop with setTimeout. */"
228           "      setTimeout(window.domAutomationController.send(%d), 0);"
229           "    } else {"
230           "      window.domAutomationController.send(%d);"
231           "    }"
232           "  };"
233           "}",
234           RETURN_CODE_OK,
235           element_id.c_str(),
236           RETURN_CODE_NO_ELEMENT,
237           RETURN_CODE_OK,
238           RETURN_CODE_WRONG_VALUE);
239   int return_value = RETURN_CODE_INVALID;
240   ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
241       RenderViewHost(), script, &return_value));
242   EXPECT_EQ(RETURN_CODE_OK, return_value)
243       << "element_id = " << element_id
244       << ", expected_value = " << expected_value;
245 }
246 
CheckElementValue(const std::string & element_id,const std::string & expected_value)247 void PasswordManagerBrowserTest::CheckElementValue(
248     const std::string& element_id,
249     const std::string& expected_value) {
250   const std::string value_check_script = base::StringPrintf(
251       "var element = document.getElementById('%s');"
252       "window.domAutomationController.send(element && element.value == '%s');",
253       element_id.c_str(),
254       expected_value.c_str());
255   bool return_value = false;
256   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
257       RenderViewHost(), value_check_script, &return_value));
258   EXPECT_TRUE(return_value) << "element_id = " << element_id
259                             << ", expected_value = " << expected_value;
260 }
261 
262 // Actual tests ---------------------------------------------------------------
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForNormalSubmit)263 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
264                        PromptForNormalSubmit) {
265   NavigateToFile("/password/password_form.html");
266 
267   // Fill a form and submit through a <input type="submit"> button. Nothing
268   // special.
269   NavigationObserver observer(WebContents());
270   std::string fill_and_submit =
271       "document.getElementById('username_field').value = 'temp';"
272       "document.getElementById('password_field').value = 'random';"
273       "document.getElementById('input_submit_button').click()";
274   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
275   observer.Wait();
276   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
277     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
278   } else {
279     EXPECT_TRUE(observer.infobar_shown());
280   }
281 }
282 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForSubmitWithInPageNavigation)283 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
284                        PromptForSubmitWithInPageNavigation) {
285   NavigateToFile("/password/password_navigate_before_submit.html");
286 
287   // Fill a form and submit through a <input type="submit"> button. Nothing
288   // special. The form does an in-page navigation before submitting.
289   NavigationObserver observer(WebContents());
290   std::string fill_and_submit =
291       "document.getElementById('username_field').value = 'temp';"
292       "document.getElementById('password_field').value = 'random';"
293       "document.getElementById('input_submit_button').click()";
294   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
295   observer.Wait();
296   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
297     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
298   } else {
299     EXPECT_TRUE(observer.infobar_shown());
300   }
301 }
302 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,LoginSuccessWithUnrelatedForm)303 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
304                        LoginSuccessWithUnrelatedForm) {
305   // Log in, see a form on the landing page. That form is not related to the
306   // login form (=has a different action), so we should offer saving the
307   // password.
308   NavigateToFile("/password/password_form.html");
309 
310   NavigationObserver observer(WebContents());
311   std::string fill_and_submit =
312       "document.getElementById('username_unrelated').value = 'temp';"
313       "document.getElementById('password_unrelated').value = 'random';"
314       "document.getElementById('submit_unrelated').click()";
315   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
316   observer.Wait();
317   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
318     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
319   } else {
320     EXPECT_TRUE(observer.infobar_shown());
321   }
322 }
323 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,LoginFailed)324 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, LoginFailed) {
325   // Log in, see a form on the landing page. That form is not related to the
326   // login form (=has a different action), so we should offer saving the
327   // password.
328   NavigateToFile("/password/password_form.html");
329 
330   NavigationObserver observer(WebContents());
331   std::string fill_and_submit =
332       "document.getElementById('username_failed').value = 'temp';"
333       "document.getElementById('password_failed').value = 'random';"
334       "document.getElementById('submit_failed').click()";
335   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
336   observer.Wait();
337   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
338     EXPECT_FALSE(controller()->PasswordPendingUserDecision());
339   } else {
340     EXPECT_FALSE(observer.infobar_shown());
341   }
342 }
343 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,Redirects)344 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, Redirects) {
345   NavigateToFile("/password/password_form.html");
346 
347   // Fill a form and submit through a <input type="submit"> button. The form
348   // points to a redirection page.
349   NavigationObserver observer(WebContents());
350   std::string fill_and_submit =
351       "document.getElementById('username_redirect').value = 'temp';"
352       "document.getElementById('password_redirect').value = 'random';"
353       "document.getElementById('submit_redirect').click()";
354   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
355   observer.disable_should_automatically_accept_infobar();
356   observer.Wait();
357   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
358     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
359   } else {
360     EXPECT_TRUE(observer.infobar_shown());
361   }
362 
363   // The redirection page now redirects via Javascript. We check that the
364   // infobar stays.
365   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
366                                      "window.location.href = 'done.html';"));
367   observer.Wait();
368   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
369     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
370   } else {
371     EXPECT_FALSE(observer.infobar_removed());
372   }
373 }
374 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForSubmitUsingJavaScript)375 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
376                        PromptForSubmitUsingJavaScript) {
377   NavigateToFile("/password/password_form.html");
378 
379   // Fill a form and submit using <button> that calls submit() on the form.
380   // This should work regardless of the type of element, as long as submit() is
381   // called.
382   NavigationObserver observer(WebContents());
383   std::string fill_and_submit =
384       "document.getElementById('username_field').value = 'temp';"
385       "document.getElementById('password_field').value = 'random';"
386       "document.getElementById('submit_button').click()";
387   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
388   observer.Wait();
389   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
390     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
391   } else {
392     EXPECT_TRUE(observer.infobar_shown());
393   }
394 }
395 
396 // Flaky: crbug.com/301547, observed on win and mac. Probably happens on all
397 // platforms.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,DISABLED_PromptForDynamicForm)398 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
399                        DISABLED_PromptForDynamicForm) {
400   NavigateToFile("/password/dynamic_password_form.html");
401 
402   // Fill the dynamic password form and submit.
403   NavigationObserver observer(WebContents());
404   std::string fill_and_submit =
405       "document.getElementById('create_form_button').click();"
406       "window.setTimeout(function() {"
407       "  document.dynamic_form.username.value = 'tempro';"
408       "  document.dynamic_form.password.value = 'random';"
409       "  document.dynamic_form.submit();"
410       "}, 0)";
411   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
412   observer.Wait();
413   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
414     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
415   } else {
416     EXPECT_TRUE(observer.infobar_shown());
417   }
418 }
419 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,NoPromptForNavigation)420 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptForNavigation) {
421   NavigateToFile("/password/password_form.html");
422 
423   // Don't fill the password form, just navigate away. Shouldn't prompt.
424   NavigationObserver observer(WebContents());
425   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(),
426                                      "window.location.href = 'done.html';"));
427   observer.Wait();
428   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
429     EXPECT_FALSE(controller()->PasswordPendingUserDecision());
430   } else {
431     EXPECT_FALSE(observer.infobar_shown());
432   }
433 }
434 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,NoPromptForSubFrameNavigation)435 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
436                        NoPromptForSubFrameNavigation) {
437   NavigateToFile("/password/multi_frames.html");
438 
439   // If you are filling out a password form in one frame and a different frame
440   // navigates, this should not trigger the infobar.
441   NavigationObserver observer(WebContents());
442   observer.SetPathToWaitFor("/password/done.html");
443   std::string fill =
444       "var first_frame = document.getElementById('first_frame');"
445       "var frame_doc = first_frame.contentDocument;"
446       "frame_doc.getElementById('username_field').value = 'temp';"
447       "frame_doc.getElementById('password_field').value = 'random';";
448   std::string navigate_frame =
449       "var second_iframe = document.getElementById('second_frame');"
450       "second_iframe.contentWindow.location.href = 'done.html';";
451 
452   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill));
453   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
454   observer.Wait();
455   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
456     EXPECT_FALSE(controller()->PasswordPendingUserDecision());
457   } else {
458     EXPECT_FALSE(observer.infobar_shown());
459   }
460 }
461 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptAfterSubmitWithSubFrameNavigation)462 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
463                        PromptAfterSubmitWithSubFrameNavigation) {
464   NavigateToFile("/password/multi_frames.html");
465 
466   // Make sure that we prompt to save password even if a sub-frame navigation
467   // happens first.
468   NavigationObserver observer(WebContents());
469   observer.SetPathToWaitFor("/password/done.html");
470   std::string navigate_frame =
471       "var second_iframe = document.getElementById('second_frame');"
472       "second_iframe.contentWindow.location.href = 'other.html';";
473   std::string fill_and_submit =
474       "var first_frame = document.getElementById('first_frame');"
475       "var frame_doc = first_frame.contentDocument;"
476       "frame_doc.getElementById('username_field').value = 'temp';"
477       "frame_doc.getElementById('password_field').value = 'random';"
478       "frame_doc.getElementById('input_submit_button').click();";
479 
480   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
481   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
482   observer.Wait();
483   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
484     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
485   } else {
486     EXPECT_TRUE(observer.infobar_shown());
487   }
488 }
489 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForXHRSubmit)490 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
491                        PromptForXHRSubmit) {
492 #if defined(OS_WIN) && defined(USE_ASH)
493   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
494   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
495     return;
496 #endif
497   NavigateToFile("/password/password_xhr_submit.html");
498 
499   // Verify that we show the save password prompt if a form returns false
500   // in its onsubmit handler but instead logs in/navigates via XHR.
501   // Note that calling 'submit()' on a form with javascript doesn't call
502   // the onsubmit handler, so we click the submit button instead.
503   NavigationObserver observer(WebContents());
504   std::string fill_and_submit =
505       "document.getElementById('username_field').value = 'temp';"
506       "document.getElementById('password_field').value = 'random';"
507       "document.getElementById('submit_button').click()";
508   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
509   observer.Wait();
510   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
511     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
512   } else {
513     EXPECT_TRUE(observer.infobar_shown());
514   }
515 }
516 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForXHRWithoutOnSubmit)517 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
518                        PromptForXHRWithoutOnSubmit) {
519   NavigateToFile("/password/password_xhr_submit.html");
520 
521   // Verify that if XHR navigation occurs and the form is properly filled out,
522   // we try and save the password even though onsubmit hasn't been called.
523   NavigationObserver observer(WebContents());
524   std::string fill_and_navigate =
525       "document.getElementById('username_field').value = 'temp';"
526       "document.getElementById('password_field').value = 'random';"
527       "send_xhr()";
528   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_navigate));
529   observer.Wait();
530   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
531     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
532   } else {
533     EXPECT_TRUE(observer.infobar_shown());
534   }
535 }
536 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,NoPromptIfLinkClicked)537 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
538                        NoPromptIfLinkClicked) {
539   NavigateToFile("/password/password_form.html");
540 
541   // Verify that if the user takes a direct action to leave the page, we don't
542   // prompt to save the password even if the form is already filled out.
543   NavigationObserver observer(WebContents());
544   std::string fill_and_click_link =
545       "document.getElementById('username_field').value = 'temp';"
546       "document.getElementById('password_field').value = 'random';"
547       "document.getElementById('link').click();";
548   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_click_link));
549   observer.Wait();
550   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
551     EXPECT_FALSE(controller()->PasswordPendingUserDecision());
552   } else {
553     EXPECT_FALSE(observer.infobar_shown());
554   }
555 }
556 
557 // TODO(jam): http://crbug.com/350550
558 #if !defined(OS_WIN)
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,VerifyPasswordGenerationUpload)559 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
560                        VerifyPasswordGenerationUpload) {
561   // Prevent Autofill requests from actually going over the wire.
562   net::TestURLFetcherFactory factory;
563   // Disable Autofill requesting access to AddressBook data. This causes
564   // the test to hang on Mac.
565   autofill::test::DisableSystemServices(browser()->profile()->GetPrefs());
566 
567   // Visit a signup form.
568   NavigateToFile("/password/signup_form.html");
569 
570   // Enter a password and save it.
571   NavigationObserver first_observer(WebContents());
572   std::string fill_and_submit =
573       "document.getElementById('other_info').value = 'stuff';"
574       "document.getElementById('username_field').value = 'my_username';"
575       "document.getElementById('password_field').value = 'password';"
576       "document.getElementById('input_submit_button').click()";
577   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
578 
579   first_observer.Wait();
580   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
581     ASSERT_TRUE(controller()->PasswordPendingUserDecision());
582     controller()->SavePassword();
583   } else {
584     ASSERT_TRUE(first_observer.infobar_shown());
585   }
586 
587   // Now navigate to a login form that has similar HTML markup.
588   NavigateToFile("/password/password_form.html");
589 
590   // Simulate a user click to force an autofill of the form's DOM value, not
591   // just the suggested value.
592   content::SimulateMouseClick(
593       WebContents(), 0, blink::WebMouseEvent::ButtonLeft);
594 
595   // The form should be filled with the previously submitted username.
596   std::string get_username =
597       "window.domAutomationController.send("
598       "document.getElementById('username_field').value);";
599   std::string actual_username;
600   ASSERT_TRUE(content::ExecuteScriptAndExtractString(RenderViewHost(),
601                                                      get_username,
602                                                      &actual_username));
603   ASSERT_EQ("my_username", actual_username);
604 
605   // Submit the form and verify that there is no infobar (as the password
606   // has already been saved).
607   NavigationObserver second_observer(WebContents());
608   std::string submit_form =
609       "document.getElementById('input_submit_button').click()";
610   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit_form));
611   second_observer.Wait();
612   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
613     EXPECT_FALSE(controller()->PasswordPendingUserDecision());
614   } else {
615     EXPECT_FALSE(second_observer.infobar_shown());
616   }
617 
618   // Verify that we sent a ping to Autofill saying that the original form
619   // was likely an account creation form since it has more than 2 text input
620   // fields and was used for the first time on a different form.
621   base::HistogramBase* upload_histogram =
622       base::StatisticsRecorder::FindHistogram(
623           "PasswordGeneration.UploadStarted");
624   ASSERT_TRUE(upload_histogram);
625   scoped_ptr<base::HistogramSamples> snapshot =
626       upload_histogram->SnapshotSamples();
627   EXPECT_EQ(0, snapshot->GetCount(0 /* failure */));
628   EXPECT_EQ(1, snapshot->GetCount(1 /* success */));
629 }
630 #endif
631 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForSubmitFromIframe)632 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForSubmitFromIframe) {
633   NavigateToFile("/password/password_submit_from_iframe.html");
634 
635   // Submit a form in an iframe, then cause the whole page to navigate without a
636   // user gesture. We expect the save password prompt to be shown here, because
637   // some pages use such iframes for login forms.
638   NavigationObserver observer(WebContents());
639   std::string fill_and_submit =
640       "var iframe = document.getElementById('test_iframe');"
641       "var iframe_doc = iframe.contentDocument;"
642       "iframe_doc.getElementById('username_field').value = 'temp';"
643       "iframe_doc.getElementById('password_field').value = 'random';"
644       "iframe_doc.getElementById('submit_button').click()";
645 
646   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
647   observer.Wait();
648   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
649     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
650   } else {
651     EXPECT_TRUE(observer.infobar_shown());
652   }
653 }
654 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForInputElementWithoutName)655 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
656                        PromptForInputElementWithoutName) {
657   // Check that the prompt is shown for forms where input elements lack the
658   // "name" attribute but the "id" is present.
659   NavigateToFile("/password/password_form.html");
660 
661   NavigationObserver observer(WebContents());
662   std::string fill_and_submit =
663       "document.getElementById('username_field_no_name').value = 'temp';"
664       "document.getElementById('password_field_no_name').value = 'random';"
665       "document.getElementById('input_submit_button_no_name').click()";
666   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
667   observer.Wait();
668   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
669     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
670   } else {
671     EXPECT_TRUE(observer.infobar_shown());
672   }
673 }
674 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptForInputElementWithoutId)675 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
676                        PromptForInputElementWithoutId) {
677   // Check that the prompt is shown for forms where input elements lack the
678   // "id" attribute but the "name" attribute is present.
679   NavigateToFile("/password/password_form.html");
680 
681   NavigationObserver observer(WebContents());
682   std::string fill_and_submit =
683       "document.getElementsByName('username_field_no_id')[0].value = 'temp';"
684       "document.getElementsByName('password_field_no_id')[0].value = 'random';"
685       "document.getElementsByName('input_submit_button_no_id')[0].click()";
686   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
687   observer.Wait();
688   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
689     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
690   } else {
691     EXPECT_TRUE(observer.infobar_shown());
692   }
693 }
694 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,NoPromptForInputElementWithoutIdAndName)695 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
696                        NoPromptForInputElementWithoutIdAndName) {
697   // Check that no prompt is shown for forms where the input fields lack both
698   // the "id" and the "name" attributes.
699   NavigateToFile("/password/password_form.html");
700 
701   NavigationObserver observer(WebContents());
702   std::string fill_and_submit =
703       "var form = document.getElementById('testform_elements_no_id_no_name');"
704       "var username = form.children[0];"
705       "username.value = 'temp';"
706       "var password = form.children[1];"
707       "password.value = 'random';"
708       "form.children[2].click()";  // form.children[2] is the submit button.
709   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
710   observer.Wait();
711   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
712     EXPECT_FALSE(controller()->PasswordPendingUserDecision());
713   } else {
714     EXPECT_FALSE(observer.infobar_shown());
715   }
716 }
717 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,DeleteFrameBeforeSubmit)718 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, DeleteFrameBeforeSubmit) {
719   NavigateToFile("/password/multi_frames.html");
720 
721   NavigationObserver observer(WebContents());
722   // Make sure we save some password info from an iframe and then destroy it.
723   std::string save_and_remove =
724       "var first_frame = document.getElementById('first_frame');"
725       "var frame_doc = first_frame.contentDocument;"
726       "frame_doc.getElementById('username_field').value = 'temp';"
727       "frame_doc.getElementById('password_field').value = 'random';"
728       "frame_doc.getElementById('input_submit_button').click();"
729       "first_frame.parentNode.removeChild(first_frame);";
730   // Submit from the main frame, but without navigating through the onsubmit
731   // handler.
732   std::string navigate_frame =
733       "document.getElementById('username_field').value = 'temp';"
734       "document.getElementById('password_field').value = 'random';"
735       "document.getElementById('input_submit_button').click();"
736       "window.location.href = 'done.html';";
737 
738   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), save_and_remove));
739   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), navigate_frame));
740   observer.Wait();
741   // The only thing we check here is that there is no use-after-free reported.
742 }
743 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PasswordValueAccessible)744 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PasswordValueAccessible) {
745   NavigateToFile("/password/form_and_link.html");
746 
747   // Click on a link to open a new tab, then switch back to the first one.
748   EXPECT_EQ(1, browser()->tab_strip_model()->count());
749   std::string click =
750       "document.getElementById('testlink').click();";
751   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), click));
752   EXPECT_EQ(2, browser()->tab_strip_model()->count());
753   browser()->tab_strip_model()->ActivateTabAt(0, false);
754 
755   // Fill in the credentials, and make sure they are saved.
756   NavigationObserver form_submit_observer(WebContents());
757   std::string fill_and_submit =
758       "document.getElementById('username_field').value = 'temp';"
759       "document.getElementById('password_field').value = 'random';"
760       "document.getElementById('input_submit_button').click();";
761   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
762   form_submit_observer.Wait();
763   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
764     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
765     controller()->SavePassword();
766   } else {
767     EXPECT_TRUE(form_submit_observer.infobar_shown());
768   }
769 
770   // Reload the original page to have the saved credentials autofilled.
771   NavigationObserver reload_observer(WebContents());
772   NavigateToFile("/password/form_and_link.html");
773   reload_observer.Wait();
774 
775   // Wait until the username is filled, to make sure autofill kicked in.
776   WaitForElementValue("username_field", "temp");
777   // Now check that the password is not accessible yet.
778   CheckElementValue("password_field", "");
779   // Let the user interact with the page.
780   content::SimulateMouseClickAt(
781       WebContents(), 0, blink::WebMouseEvent::ButtonLeft, gfx::Point(1, 1));
782   // Wait until that interaction causes the password value to be revealed.
783   WaitForElementValue("password_field", "random");
784   // And check that after the side-effects of the interaction took place, the
785   // username value stays the same.
786   CheckElementValue("username_field", "temp");
787 }
788 
789 // The following test is limited to Aura, because
790 // RenderWidgetHostViewGuest::ProcessAckedTouchEvent is, and
791 // ProcessAckedTouchEvent is what triggers the translation of touch events to
792 // gesture events.
793 #if defined(USE_AURA)
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PasswordValueAccessibleOnSubmit)794 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
795                        PasswordValueAccessibleOnSubmit) {
796   NavigateToFile("/password/form_and_link.html");
797 
798   // Fill in the credentials, and make sure they are saved.
799   NavigationObserver form_submit_observer(WebContents());
800   std::string fill_and_submit =
801       "document.getElementById('username_field').value = 'temp';"
802       "document.getElementById('password_field').value = 'random_secret';"
803       "document.getElementById('input_submit_button').click();";
804   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
805   form_submit_observer.Wait();
806   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
807     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
808     controller()->SavePassword();
809   } else {
810     EXPECT_TRUE(form_submit_observer.infobar_shown());
811   }
812 
813   // Reload the original page to have the saved credentials autofilled.
814   NavigationObserver reload_observer(WebContents());
815   NavigateToFile("/password/form_and_link.html");
816   reload_observer.Wait();
817 
818   NavigationObserver submit_observer(WebContents());
819   // Submit the form via a tap on the submit button. The button is placed at 0,
820   // 100, and has height 300 and width 700.
821   content::SimulateTapAt(WebContents(), gfx::Point(350, 250));
822   submit_observer.Wait();
823   std::string query = WebContents()->GetURL().query();
824   EXPECT_NE(std::string::npos, query.find("random_secret")) << query;
825 }
826 #endif
827 
828 // Test fix for crbug.com/338650.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,DontPromptForPasswordFormWithDefaultValue)829 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
830                        DontPromptForPasswordFormWithDefaultValue) {
831   NavigateToFile("/password/password_form_with_default_value.html");
832 
833   // Don't prompt if we navigate away even if there is a password value since
834   // it's not coming from the user.
835   NavigationObserver observer(WebContents());
836   NavigateToFile("/password/done.html");
837   observer.Wait();
838   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
839     EXPECT_FALSE(controller()->PasswordPendingUserDecision());
840   } else {
841     EXPECT_FALSE(observer.infobar_shown());
842   }
843 }
844 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,PromptWhenEnableAutomaticPasswordSavingSwitchIsNotSet)845 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
846                        PromptWhenEnableAutomaticPasswordSavingSwitchIsNotSet) {
847   NavigateToFile("/password/password_form.html");
848 
849   // Fill a form and submit through a <input type="submit"> button.
850   NavigationObserver observer(WebContents());
851   std::string fill_and_submit =
852       "document.getElementById('username_field').value = 'temp';"
853       "document.getElementById('password_field').value = 'random';"
854       "document.getElementById('input_submit_button').click()";
855   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
856   observer.Wait();
857   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
858     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
859   } else {
860     EXPECT_TRUE(observer.infobar_shown());
861   }
862 }
863 
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,DontPromptWhenEnableAutomaticPasswordSavingSwitchIsSet)864 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
865                        DontPromptWhenEnableAutomaticPasswordSavingSwitchIsSet) {
866   password_manager::TestPasswordStore* password_store =
867       static_cast<password_manager::TestPasswordStore*>(
868           PasswordStoreFactory::GetForProfile(browser()->profile(),
869                                               Profile::IMPLICIT_ACCESS).get());
870 
871   EXPECT_TRUE(password_store->IsEmpty());
872 
873   NavigateToFile("/password/password_form.html");
874 
875   // Add the enable-automatic-password-saving switch.
876   CommandLine::ForCurrentProcess()->AppendSwitch(
877       password_manager::switches::kEnableAutomaticPasswordSaving);
878 
879   // Fill a form and submit through a <input type="submit"> button.
880   NavigationObserver observer(WebContents());
881   // Make sure that the only passwords saved are the auto-saved ones.
882   observer.disable_should_automatically_accept_infobar();
883   std::string fill_and_submit =
884       "document.getElementById('username_field').value = 'temp';"
885       "document.getElementById('password_field').value = 'random';"
886       "document.getElementById('input_submit_button').click()";
887   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill_and_submit));
888   observer.Wait();
889   if (chrome::VersionInfo::GetChannel() ==
890       chrome::VersionInfo::CHANNEL_UNKNOWN) {
891     if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
892       EXPECT_FALSE(controller()->PasswordPendingUserDecision());
893     } else {
894       EXPECT_FALSE(observer.infobar_shown());
895     }
896     EXPECT_FALSE(password_store->IsEmpty());
897   } else {
898     if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
899       EXPECT_TRUE(controller()->PasswordPendingUserDecision());
900     } else {
901       EXPECT_TRUE(observer.infobar_shown());
902     }
903     EXPECT_TRUE(password_store->IsEmpty());
904   }
905 }
906 
907 // Test fix for crbug.com/368690.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,NoPromptWhenReloading)908 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, NoPromptWhenReloading) {
909   NavigateToFile("/password/password_form.html");
910 
911   std::string fill =
912       "document.getElementById('username_redirect').value = 'temp';"
913       "document.getElementById('password_redirect').value = 'random';";
914   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), fill));
915 
916   NavigationObserver observer(WebContents());
917   GURL url = embedded_test_server()->GetURL("/password/password_form.html");
918   chrome::NavigateParams params(browser(), url,
919                                 content::PAGE_TRANSITION_RELOAD);
920   ui_test_utils::NavigateToURL(&params);
921   observer.Wait();
922   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
923     EXPECT_FALSE(controller()->PasswordPendingUserDecision());
924   } else {
925     EXPECT_FALSE(observer.infobar_shown());
926   }
927 }
928 
929 // Test that if a form gets dynamically added between the form parsing and
930 // rendering, and while the main frame still loads, it still is registered, and
931 // thus saving passwords from it works.
IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,FormsAddedBetweenParsingAndRendering)932 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
933                        FormsAddedBetweenParsingAndRendering) {
934   NavigateToFile("/password/between_parsing_and_rendering.html");
935 
936   NavigationObserver observer(WebContents());
937   std::string submit =
938       "document.getElementById('username').value = 'temp';"
939       "document.getElementById('password').value = 'random';"
940       "document.getElementById('submit-button').click();";
941   ASSERT_TRUE(content::ExecuteScript(RenderViewHost(), submit));
942   observer.Wait();
943 
944   if (ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled()) {
945     EXPECT_TRUE(controller()->PasswordPendingUserDecision());
946   } else {
947     EXPECT_TRUE(observer.infobar_shown());
948   }
949 }
950