Home | History | Annotate | Download | only in password_manager
      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:
     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 
     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.
     69   void SetPathToWaitFor(const std::string& path) {
     70     wait_for_path_ = path;
     71   }
     72 
     73   // content::WebContentsObserver:
     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 
     87   bool infobar_shown() const { return infobar_shown_; }
     88   bool infobar_removed() const { return infobar_removed_; }
     89 
     90   void disable_should_automatically_accept_infobar() {
     91     should_automatically_accept_infobar_ = false;
     92   }
     93 
     94   void Wait() {
     95     message_loop_runner_->Run();
     96   }
     97 
     98  private:
     99   // infobars::InfoBarManager::Observer:
    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 
    108   virtual void OnInfoBarRemoved(infobars::InfoBar* infobar,
    109                                 bool animate) OVERRIDE {
    110     infobar_removed_ = true;
    111   }
    112 
    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:
    139   PasswordManagerBrowserTest() {}
    140   virtual ~PasswordManagerBrowserTest() {}
    141 
    142   // InProcessBrowserTest:
    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:
    154   content::WebContents* WebContents() {
    155     return browser()->tab_strip_model()->GetActiveWebContents();
    156   }
    157 
    158   content::RenderViewHost* RenderViewHost() {
    159     return WebContents()->GetRenderViewHost();
    160   }
    161 
    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.
    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 
    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 
    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 ---------------------------------------------------------------
    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 
    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 
    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 
    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 
    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 
    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.
    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 
    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 
    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 
    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 
    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 
    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 
    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)
    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 
    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 
    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 
    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 
    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 
    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 
    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)
    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.
    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 
    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 
    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.
    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.
    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