Home | History | Annotate | Download | only in login
      1 // Copyright (c) 2012 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 <algorithm>
      6 #include <list>
      7 #include <map>
      8 
      9 #include "base/metrics/field_trial.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "chrome/browser/chrome_notification_types.h"
     12 #include "chrome/browser/prerender/prerender_manager.h"
     13 #include "chrome/browser/ui/browser.h"
     14 #include "chrome/browser/ui/browser_commands.h"
     15 #include "chrome/browser/ui/login/login_prompt.h"
     16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     17 #include "chrome/test/base/in_process_browser_test.h"
     18 #include "chrome/test/base/ui_test_utils.h"
     19 #include "content/public/browser/notification_details.h"
     20 #include "content/public/browser/notification_source.h"
     21 #include "content/public/browser/web_contents.h"
     22 #include "content/public/test/browser_test_utils.h"
     23 #include "net/base/auth.h"
     24 #include "net/dns/mock_host_resolver.h"
     25 
     26 using content::NavigationController;
     27 using content::OpenURLParams;
     28 using content::Referrer;
     29 
     30 namespace {
     31 
     32 class LoginPromptBrowserTest : public InProcessBrowserTest {
     33  public:
     34   LoginPromptBrowserTest()
     35       : bad_password_("incorrect"),
     36         bad_username_("nouser"),
     37         password_("secret"),
     38         username_basic_("basicuser"),
     39         username_digest_("digestuser") {
     40     auth_map_["foo"] = AuthInfo("testuser", "foopassword");
     41     auth_map_["bar"] = AuthInfo("testuser", "barpassword");
     42     auth_map_["testrealm"] = AuthInfo(username_basic_, password_);
     43   }
     44 
     45  protected:
     46   struct AuthInfo {
     47     std::string username_;
     48     std::string password_;
     49 
     50     AuthInfo() {}
     51 
     52     AuthInfo(const std::string& username,
     53              const std::string& password)
     54         : username_(username), password_(password) {}
     55   };
     56 
     57   typedef std::map<std::string, AuthInfo> AuthMap;
     58 
     59   void SetAuthFor(LoginHandler* handler);
     60 
     61   AuthMap auth_map_;
     62   std::string bad_password_;
     63   std::string bad_username_;
     64   std::string password_;
     65   std::string username_basic_;
     66   std::string username_digest_;
     67 };
     68 
     69 void LoginPromptBrowserTest::SetAuthFor(LoginHandler* handler) {
     70   const net::AuthChallengeInfo* challenge = handler->auth_info();
     71 
     72   ASSERT_TRUE(challenge);
     73   AuthMap::iterator i = auth_map_.find(challenge->realm);
     74   EXPECT_TRUE(auth_map_.end() != i);
     75   if (i != auth_map_.end()) {
     76     const AuthInfo& info = i->second;
     77     handler->SetAuth(UTF8ToUTF16(info.username_),
     78                      UTF8ToUTF16(info.password_));
     79   }
     80 }
     81 
     82 // Maintains a set of LoginHandlers that are currently active and
     83 // keeps a count of the notifications that were observed.
     84 class LoginPromptBrowserTestObserver : public content::NotificationObserver {
     85  public:
     86   LoginPromptBrowserTestObserver()
     87       : auth_needed_count_(0),
     88         auth_supplied_count_(0),
     89         auth_cancelled_count_(0) {}
     90 
     91   virtual void Observe(int type,
     92                        const content::NotificationSource& source,
     93                        const content::NotificationDetails& details) OVERRIDE;
     94 
     95   void AddHandler(LoginHandler* handler);
     96 
     97   void RemoveHandler(LoginHandler* handler);
     98 
     99   void Register(const content::NotificationSource& source);
    100 
    101   std::list<LoginHandler*> handlers_;
    102 
    103   // The exact number of notifications we receive is depedent on the
    104   // number of requests that were dispatched and is subject to a
    105   // number of factors that we don't directly control here.  The
    106   // values below should only be used qualitatively.
    107   int auth_needed_count_;
    108   int auth_supplied_count_;
    109   int auth_cancelled_count_;
    110 
    111  private:
    112   content::NotificationRegistrar registrar_;
    113 
    114   DISALLOW_COPY_AND_ASSIGN(LoginPromptBrowserTestObserver);
    115 };
    116 
    117 void LoginPromptBrowserTestObserver::Observe(
    118     int type,
    119     const content::NotificationSource& source,
    120     const content::NotificationDetails& details) {
    121   if (type == chrome::NOTIFICATION_AUTH_NEEDED) {
    122     LoginNotificationDetails* login_details =
    123         content::Details<LoginNotificationDetails>(details).ptr();
    124     AddHandler(login_details->handler());
    125     auth_needed_count_++;
    126   } else if (type == chrome::NOTIFICATION_AUTH_SUPPLIED) {
    127     AuthSuppliedLoginNotificationDetails* login_details =
    128         content::Details<AuthSuppliedLoginNotificationDetails>(details).ptr();
    129     RemoveHandler(login_details->handler());
    130     auth_supplied_count_++;
    131   } else if (type == chrome::NOTIFICATION_AUTH_CANCELLED) {
    132     LoginNotificationDetails* login_details =
    133         content::Details<LoginNotificationDetails>(details).ptr();
    134     RemoveHandler(login_details->handler());
    135     auth_cancelled_count_++;
    136   }
    137 }
    138 
    139 void LoginPromptBrowserTestObserver::AddHandler(LoginHandler* handler) {
    140   std::list<LoginHandler*>::iterator i = std::find(handlers_.begin(),
    141                                                    handlers_.end(),
    142                                                    handler);
    143   EXPECT_TRUE(i == handlers_.end());
    144   if (i == handlers_.end())
    145     handlers_.push_back(handler);
    146 }
    147 
    148 void LoginPromptBrowserTestObserver::RemoveHandler(LoginHandler* handler) {
    149   std::list<LoginHandler*>::iterator i = std::find(handlers_.begin(),
    150                                                    handlers_.end(),
    151                                                    handler);
    152   EXPECT_TRUE(i != handlers_.end());
    153   if (i != handlers_.end())
    154     handlers_.erase(i);
    155 }
    156 
    157 void LoginPromptBrowserTestObserver::Register(
    158     const content::NotificationSource& source) {
    159   registrar_.Add(this, chrome::NOTIFICATION_AUTH_NEEDED, source);
    160   registrar_.Add(this, chrome::NOTIFICATION_AUTH_SUPPLIED, source);
    161   registrar_.Add(this, chrome::NOTIFICATION_AUTH_CANCELLED, source);
    162 }
    163 
    164 template <int T>
    165 class WindowedNavigationObserver
    166     : public content::WindowedNotificationObserver {
    167  public:
    168   explicit WindowedNavigationObserver(NavigationController* controller)
    169       : content::WindowedNotificationObserver(
    170           T, content::Source<NavigationController>(controller)) {}
    171 };
    172 
    173 // LOAD_STOP observer is special since we want to be able to wait for
    174 // multiple LOAD_STOP events.
    175 class WindowedLoadStopObserver
    176     : public WindowedNavigationObserver<content::NOTIFICATION_LOAD_STOP> {
    177  public:
    178   WindowedLoadStopObserver(NavigationController* controller,
    179                            int notification_count)
    180       : WindowedNavigationObserver<content::NOTIFICATION_LOAD_STOP>(controller),
    181         remaining_notification_count_(notification_count) {}
    182  protected:
    183   virtual void Observe(int type,
    184                        const content::NotificationSource& source,
    185                        const content::NotificationDetails& details) OVERRIDE;
    186  private:
    187   int remaining_notification_count_;  // Number of notifications remaining.
    188 };
    189 
    190 void WindowedLoadStopObserver::Observe(
    191     int type,
    192     const content::NotificationSource& source,
    193     const content::NotificationDetails& details) {
    194   if (--remaining_notification_count_ == 0)
    195     WindowedNotificationObserver::Observe(type, source, details);
    196 }
    197 
    198 typedef WindowedNavigationObserver<chrome::NOTIFICATION_AUTH_NEEDED>
    199     WindowedAuthNeededObserver;
    200 
    201 typedef WindowedNavigationObserver<chrome::NOTIFICATION_AUTH_CANCELLED>
    202     WindowedAuthCancelledObserver;
    203 
    204 typedef WindowedNavigationObserver<chrome::NOTIFICATION_AUTH_SUPPLIED>
    205     WindowedAuthSuppliedObserver;
    206 
    207 const char kPrefetchAuthPage[] = "files/login/prefetch.html";
    208 
    209 const char kMultiRealmTestPage[] = "files/login/multi_realm.html";
    210 const int  kMultiRealmTestRealmCount = 2;
    211 
    212 const char kSingleRealmTestPage[] = "files/login/single_realm.html";
    213 
    214 const char* kAuthBasicPage = "auth-basic";
    215 const char* kAuthDigestPage = "auth-digest";
    216 
    217 base::string16 ExpectedTitleFromAuth(const base::string16& username,
    218                                      const base::string16& password) {
    219   // The TestServer sets the title to username/password on successful login.
    220   return username + UTF8ToUTF16("/") + password;
    221 }
    222 
    223 // Confirm that <link rel="prefetch"> targetting an auth required
    224 // resource does not provide a login dialog.  These types of requests
    225 // should instead just cancel the auth.
    226 
    227 // Unfortunately, this test doesn't assert on anything for its
    228 // correctness.  Instead, it relies on the auth dialog blocking the
    229 // browser, and triggering a timeout to cause failure when the
    230 // prefetch resource requires authorization.
    231 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, PrefetchAuthCancels) {
    232   ASSERT_TRUE(test_server()->Start());
    233 
    234   GURL test_page = test_server()->GetURL(kPrefetchAuthPage);
    235 
    236   class SetPrefetchForTest {
    237    public:
    238     explicit SetPrefetchForTest(bool prefetch)
    239         : old_prerender_mode_(prerender::PrerenderManager::GetMode()) {
    240       std::string exp_group = prefetch ? "ExperimentYes" : "ExperimentNo";
    241       base::FieldTrialList::CreateFieldTrial("Prefetch", exp_group);
    242       // Disable prerender so this is just a prefetch of the top-level page.
    243       prerender::PrerenderManager::SetMode(
    244           prerender::PrerenderManager::PRERENDER_MODE_DISABLED);
    245     }
    246 
    247     ~SetPrefetchForTest() {
    248       prerender::PrerenderManager::SetMode(old_prerender_mode_);
    249     }
    250 
    251    private:
    252     prerender::PrerenderManager::PrerenderManagerMode old_prerender_mode_;
    253   } set_prefetch_for_test(true);
    254 
    255   content::WebContents* contents =
    256       browser()->tab_strip_model()->GetActiveWebContents();
    257   NavigationController* controller = &contents->GetController();
    258   LoginPromptBrowserTestObserver observer;
    259 
    260   observer.Register(content::Source<NavigationController>(controller));
    261 
    262   WindowedLoadStopObserver load_stop_waiter(controller, 1);
    263   browser()->OpenURL(OpenURLParams(
    264       test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    265       false));
    266 
    267   load_stop_waiter.Wait();
    268   EXPECT_TRUE(observer.handlers_.empty());
    269   EXPECT_TRUE(test_server()->Stop());
    270 }
    271 
    272 // Test that "Basic" HTTP authentication works.
    273 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestBasicAuth) {
    274   ASSERT_TRUE(test_server()->Start());
    275   GURL test_page = test_server()->GetURL(kAuthBasicPage);
    276 
    277   content::WebContents* contents =
    278       browser()->tab_strip_model()->GetActiveWebContents();
    279   NavigationController* controller = &contents->GetController();
    280   LoginPromptBrowserTestObserver observer;
    281 
    282   observer.Register(content::Source<NavigationController>(controller));
    283 
    284   {
    285     WindowedAuthNeededObserver auth_needed_waiter(controller);
    286     browser()->OpenURL(OpenURLParams(
    287         test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    288         false));
    289     auth_needed_waiter.Wait();
    290   }
    291 
    292   ASSERT_FALSE(observer.handlers_.empty());
    293   {
    294     WindowedAuthNeededObserver auth_needed_waiter(controller);
    295     WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
    296     LoginHandler* handler = *observer.handlers_.begin();
    297 
    298     ASSERT_TRUE(handler);
    299     handler->SetAuth(UTF8ToUTF16(bad_username_), UTF8ToUTF16(bad_password_));
    300     auth_supplied_waiter.Wait();
    301 
    302     // The request should be retried after the incorrect password is
    303     // supplied.  This should result in a new AUTH_NEEDED notification
    304     // for the same realm.
    305     auth_needed_waiter.Wait();
    306   }
    307 
    308   ASSERT_EQ(1u, observer.handlers_.size());
    309   WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
    310   LoginHandler* handler = *observer.handlers_.begin();
    311   SetAuthFor(handler);
    312   auth_supplied_waiter.Wait();
    313 
    314   base::string16 expected_title =
    315       ExpectedTitleFromAuth(ASCIIToUTF16("basicuser"), ASCIIToUTF16("secret"));
    316   content::TitleWatcher title_watcher(contents, expected_title);
    317   EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
    318 }
    319 
    320 // Test that "Digest" HTTP authentication works.
    321 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestDigestAuth) {
    322   ASSERT_TRUE(test_server()->Start());
    323   GURL test_page = test_server()->GetURL(kAuthDigestPage);
    324 
    325   content::WebContents* contents =
    326       browser()->tab_strip_model()->GetActiveWebContents();
    327   NavigationController* controller = &contents->GetController();
    328   LoginPromptBrowserTestObserver observer;
    329 
    330   observer.Register(content::Source<NavigationController>(controller));
    331 
    332   {
    333     WindowedAuthNeededObserver auth_needed_waiter(controller);
    334     browser()->OpenURL(OpenURLParams(
    335         test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    336         false));
    337     auth_needed_waiter.Wait();
    338   }
    339 
    340   ASSERT_FALSE(observer.handlers_.empty());
    341   {
    342     WindowedAuthNeededObserver auth_needed_waiter(controller);
    343     WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
    344     LoginHandler* handler = *observer.handlers_.begin();
    345 
    346     ASSERT_TRUE(handler);
    347     handler->SetAuth(UTF8ToUTF16(bad_username_), UTF8ToUTF16(bad_password_));
    348     auth_supplied_waiter.Wait();
    349 
    350     // The request should be retried after the incorrect password is
    351     // supplied.  This should result in a new AUTH_NEEDED notification
    352     // for the same realm.
    353     auth_needed_waiter.Wait();
    354   }
    355 
    356   ASSERT_EQ(1u, observer.handlers_.size());
    357   WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
    358   LoginHandler* handler = *observer.handlers_.begin();
    359 
    360   base::string16 username(UTF8ToUTF16(username_digest_));
    361   base::string16 password(UTF8ToUTF16(password_));
    362   handler->SetAuth(username, password);
    363   auth_supplied_waiter.Wait();
    364 
    365   base::string16 expected_title = ExpectedTitleFromAuth(username, password);
    366   content::TitleWatcher title_watcher(contents, expected_title);
    367   EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
    368 }
    369 
    370 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestTwoAuths) {
    371   ASSERT_TRUE(test_server()->Start());
    372 
    373   content::WebContents* contents1 =
    374       browser()->tab_strip_model()->GetActiveWebContents();
    375   NavigationController* controller1 = &contents1->GetController();
    376   LoginPromptBrowserTestObserver observer;
    377 
    378   observer.Register(content::Source<NavigationController>(controller1));
    379 
    380   // Open a new tab.
    381   ui_test_utils::NavigateToURLWithDisposition(
    382       browser(),
    383       GURL("about:blank"),
    384       NEW_FOREGROUND_TAB,
    385       ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
    386 
    387   content::WebContents* contents2 =
    388       browser()->tab_strip_model()->GetActiveWebContents();
    389   ASSERT_NE(contents1, contents2);
    390   NavigationController* controller2 = &contents2->GetController();
    391   observer.Register(content::Source<NavigationController>(controller2));
    392 
    393   {
    394     WindowedAuthNeededObserver auth_needed_waiter(controller1);
    395     contents1->OpenURL(OpenURLParams(
    396         test_server()->GetURL(kAuthBasicPage), Referrer(),
    397         CURRENT_TAB, content::PAGE_TRANSITION_TYPED, false));
    398     auth_needed_waiter.Wait();
    399   }
    400 
    401   {
    402     WindowedAuthNeededObserver auth_needed_waiter(controller2);
    403     contents2->OpenURL(OpenURLParams(
    404         test_server()->GetURL(kAuthDigestPage), Referrer(),
    405         CURRENT_TAB, content::PAGE_TRANSITION_TYPED, false));
    406     auth_needed_waiter.Wait();
    407   }
    408 
    409   ASSERT_EQ(2u, observer.handlers_.size());
    410 
    411   LoginHandler* handler1 = *observer.handlers_.begin();
    412   LoginHandler* handler2 = *(++(observer.handlers_.begin()));
    413 
    414   base::string16 expected_title1 = ExpectedTitleFromAuth(
    415       UTF8ToUTF16(username_basic_), UTF8ToUTF16(password_));
    416   base::string16 expected_title2 = ExpectedTitleFromAuth(
    417       UTF8ToUTF16(username_digest_), UTF8ToUTF16(password_));
    418   content::TitleWatcher title_watcher1(contents1, expected_title1);
    419   content::TitleWatcher title_watcher2(contents2, expected_title2);
    420 
    421   handler1->SetAuth(UTF8ToUTF16(username_basic_), UTF8ToUTF16(password_));
    422   handler2->SetAuth(UTF8ToUTF16(username_digest_), UTF8ToUTF16(password_));
    423 
    424   EXPECT_EQ(expected_title1, title_watcher1.WaitAndGetTitle());
    425   EXPECT_EQ(expected_title2, title_watcher2.WaitAndGetTitle());
    426 }
    427 
    428 // Test login prompt cancellation.
    429 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestCancelAuth) {
    430   ASSERT_TRUE(test_server()->Start());
    431   GURL auth_page = test_server()->GetURL(kAuthBasicPage);
    432   GURL no_auth_page_1 = test_server()->GetURL("a");
    433   GURL no_auth_page_2 = test_server()->GetURL("b");
    434   GURL no_auth_page_3 = test_server()->GetURL("c");
    435 
    436   content::WebContents* contents =
    437       browser()->tab_strip_model()->GetActiveWebContents();
    438   NavigationController* controller = &contents->GetController();
    439 
    440   LoginPromptBrowserTestObserver observer;
    441   observer.Register(content::Source<NavigationController>(controller));
    442 
    443   // First navigate to an unauthenticated page so we have something to
    444   // go back to.
    445   ui_test_utils::NavigateToURL(browser(), no_auth_page_1);
    446 
    447   // Navigating while auth is requested is the same as cancelling.
    448   {
    449     // We need to wait for two LOAD_STOP events.  One for auth_page and one for
    450     // no_auth_page_2.
    451     WindowedLoadStopObserver load_stop_waiter(controller, 2);
    452     WindowedAuthNeededObserver auth_needed_waiter(controller);
    453     browser()->OpenURL(OpenURLParams(
    454         auth_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    455         false));
    456     auth_needed_waiter.Wait();
    457     WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
    458     browser()->OpenURL(OpenURLParams(
    459         no_auth_page_2, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    460         false));
    461     auth_cancelled_waiter.Wait();
    462     load_stop_waiter.Wait();
    463     EXPECT_TRUE(observer.handlers_.empty());
    464   }
    465 
    466   // Try navigating backwards.
    467   {
    468     // As above, we wait for two LOAD_STOP events; one for each navigation.
    469     WindowedLoadStopObserver load_stop_waiter(controller, 2);
    470     WindowedAuthNeededObserver auth_needed_waiter(controller);
    471     browser()->OpenURL(OpenURLParams(
    472         auth_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    473         false));
    474     auth_needed_waiter.Wait();
    475     WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
    476     ASSERT_TRUE(chrome::CanGoBack(browser()));
    477     chrome::GoBack(browser(), CURRENT_TAB);
    478     auth_cancelled_waiter.Wait();
    479     load_stop_waiter.Wait();
    480     EXPECT_TRUE(observer.handlers_.empty());
    481   }
    482 
    483   // Now add a page and go back, so we have something to go forward to.
    484   ui_test_utils::NavigateToURL(browser(), no_auth_page_3);
    485   {
    486     WindowedLoadStopObserver load_stop_waiter(controller, 1);
    487     chrome::GoBack(browser(), CURRENT_TAB);  // Should take us to page 1
    488     load_stop_waiter.Wait();
    489   }
    490 
    491   {
    492     // We wait for two LOAD_STOP events; one for each navigation.
    493     WindowedLoadStopObserver load_stop_waiter(controller, 2);
    494     WindowedAuthNeededObserver auth_needed_waiter(controller);
    495     browser()->OpenURL(OpenURLParams(
    496         auth_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    497         false));
    498     auth_needed_waiter.Wait();
    499     WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
    500     ASSERT_TRUE(chrome::CanGoForward(browser()));
    501     chrome::GoForward(browser(), CURRENT_TAB);  // Should take us to page 3
    502     auth_cancelled_waiter.Wait();
    503     load_stop_waiter.Wait();
    504     EXPECT_TRUE(observer.handlers_.empty());
    505   }
    506 
    507   // Now test that cancelling works as expected.
    508   {
    509     WindowedLoadStopObserver load_stop_waiter(controller, 1);
    510     WindowedAuthNeededObserver auth_needed_waiter(controller);
    511     browser()->OpenURL(OpenURLParams(
    512         auth_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    513         false));
    514     auth_needed_waiter.Wait();
    515     WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
    516     LoginHandler* handler = *observer.handlers_.begin();
    517     ASSERT_TRUE(handler);
    518     handler->CancelAuth();
    519     auth_cancelled_waiter.Wait();
    520     load_stop_waiter.Wait();
    521     EXPECT_TRUE(observer.handlers_.empty());
    522   }
    523 }
    524 
    525 // Test handling of resources that require authentication even though
    526 // the page they are included on doesn't.  In this case we should only
    527 // present the minimal number of prompts necessary for successfully
    528 // displaying the page.  First we check whether cancelling works as
    529 // expected.
    530 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmCancellation) {
    531   ASSERT_TRUE(test_server()->Start());
    532   GURL test_page = test_server()->GetURL(kMultiRealmTestPage);
    533 
    534   content::WebContents* contents =
    535       browser()->tab_strip_model()->GetActiveWebContents();
    536   NavigationController* controller = &contents->GetController();
    537   LoginPromptBrowserTestObserver observer;
    538 
    539   observer.Register(content::Source<NavigationController>(controller));
    540 
    541   WindowedLoadStopObserver load_stop_waiter(controller, 1);
    542 
    543   {
    544     WindowedAuthNeededObserver auth_needed_waiter(controller);
    545     browser()->OpenURL(OpenURLParams(
    546         test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    547         false));
    548     auth_needed_waiter.Wait();
    549   }
    550 
    551   int n_handlers = 0;
    552 
    553   while (n_handlers < kMultiRealmTestRealmCount) {
    554     WindowedAuthNeededObserver auth_needed_waiter(controller);
    555 
    556     while (!observer.handlers_.empty()) {
    557       WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
    558       LoginHandler* handler = *observer.handlers_.begin();
    559 
    560       ASSERT_TRUE(handler);
    561       n_handlers++;
    562       handler->CancelAuth();
    563       auth_cancelled_waiter.Wait();
    564     }
    565 
    566     if (n_handlers < kMultiRealmTestRealmCount)
    567       auth_needed_waiter.Wait();
    568   }
    569 
    570   load_stop_waiter.Wait();
    571 
    572   EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers);
    573   EXPECT_EQ(0, observer.auth_supplied_count_);
    574   EXPECT_LT(0, observer.auth_needed_count_);
    575   EXPECT_LT(0, observer.auth_cancelled_count_);
    576   EXPECT_TRUE(test_server()->Stop());
    577 }
    578 
    579 // Similar to the MultipleRealmCancellation test above, but tests
    580 // whether supplying credentials work as exepcted.
    581 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmConfirmation) {
    582   ASSERT_TRUE(test_server()->Start());
    583   GURL test_page = test_server()->GetURL(kMultiRealmTestPage);
    584 
    585   content::WebContents* contents =
    586       browser()->tab_strip_model()->GetActiveWebContents();
    587   NavigationController* controller = &contents->GetController();
    588   LoginPromptBrowserTestObserver observer;
    589 
    590   observer.Register(content::Source<NavigationController>(controller));
    591 
    592   WindowedLoadStopObserver load_stop_waiter(controller, 1);
    593   int n_handlers = 0;
    594 
    595   {
    596     WindowedAuthNeededObserver auth_needed_waiter(controller);
    597 
    598     browser()->OpenURL(OpenURLParams(
    599         test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    600         false));
    601     auth_needed_waiter.Wait();
    602   }
    603 
    604   while (n_handlers < kMultiRealmTestRealmCount) {
    605     WindowedAuthNeededObserver auth_needed_waiter(controller);
    606 
    607     while (!observer.handlers_.empty()) {
    608       WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
    609       LoginHandler* handler = *observer.handlers_.begin();
    610 
    611       ASSERT_TRUE(handler);
    612       n_handlers++;
    613       SetAuthFor(handler);
    614       auth_supplied_waiter.Wait();
    615     }
    616 
    617     if (n_handlers < kMultiRealmTestRealmCount)
    618       auth_needed_waiter.Wait();
    619   }
    620 
    621   load_stop_waiter.Wait();
    622 
    623   EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers);
    624   EXPECT_LT(0, observer.auth_needed_count_);
    625   EXPECT_LT(0, observer.auth_supplied_count_);
    626   EXPECT_EQ(0, observer.auth_cancelled_count_);
    627   EXPECT_TRUE(test_server()->Stop());
    628 }
    629 
    630 // Testing for recovery from an incorrect password for the case where
    631 // there are multiple authenticated resources.
    632 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, IncorrectConfirmation) {
    633   ASSERT_TRUE(test_server()->Start());
    634   GURL test_page = test_server()->GetURL(kSingleRealmTestPage);
    635 
    636   content::WebContents* contents =
    637       browser()->tab_strip_model()->GetActiveWebContents();
    638   NavigationController* controller = &contents->GetController();
    639   LoginPromptBrowserTestObserver observer;
    640 
    641   observer.Register(content::Source<NavigationController>(controller));
    642 
    643   {
    644     WindowedAuthNeededObserver auth_needed_waiter(controller);
    645     browser()->OpenURL(OpenURLParams(
    646         test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    647         false));
    648     auth_needed_waiter.Wait();
    649   }
    650 
    651   EXPECT_FALSE(observer.handlers_.empty());
    652 
    653   if (!observer.handlers_.empty()) {
    654     WindowedAuthNeededObserver auth_needed_waiter(controller);
    655     WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
    656     LoginHandler* handler = *observer.handlers_.begin();
    657 
    658     ASSERT_TRUE(handler);
    659     handler->SetAuth(UTF8ToUTF16(bad_username_),
    660                      UTF8ToUTF16(bad_password_));
    661     auth_supplied_waiter.Wait();
    662 
    663     // The request should be retried after the incorrect password is
    664     // supplied.  This should result in a new AUTH_NEEDED notification
    665     // for the same realm.
    666     auth_needed_waiter.Wait();
    667   }
    668 
    669   int n_handlers = 0;
    670 
    671   while (n_handlers < 1) {
    672     WindowedAuthNeededObserver auth_needed_waiter(controller);
    673 
    674     while (!observer.handlers_.empty()) {
    675       WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
    676       LoginHandler* handler = *observer.handlers_.begin();
    677 
    678       ASSERT_TRUE(handler);
    679       n_handlers++;
    680       SetAuthFor(handler);
    681       auth_supplied_waiter.Wait();
    682     }
    683 
    684     if (n_handlers < 1)
    685       auth_needed_waiter.Wait();
    686   }
    687 
    688   // The single realm test has only one realm, and thus only one login
    689   // prompt.
    690   EXPECT_EQ(1, n_handlers);
    691   EXPECT_LT(0, observer.auth_needed_count_);
    692   EXPECT_EQ(0, observer.auth_cancelled_count_);
    693   EXPECT_EQ(observer.auth_needed_count_, observer.auth_supplied_count_);
    694   EXPECT_TRUE(test_server()->Stop());
    695 }
    696 
    697 // If the favicon is an authenticated resource, we shouldn't prompt
    698 // for credentials.  The same URL, if requested elsewhere should
    699 // prompt for credentials.
    700 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, NoLoginPromptForFavicon) {
    701   const char* kFaviconTestPage = "files/login/has_favicon.html";
    702   const char* kFaviconResource = "auth-basic/favicon.gif";
    703 
    704   ASSERT_TRUE(test_server()->Start());
    705 
    706   content::WebContents* contents =
    707       browser()->tab_strip_model()->GetActiveWebContents();
    708   NavigationController* controller = &contents->GetController();
    709   LoginPromptBrowserTestObserver observer;
    710 
    711   observer.Register(content::Source<NavigationController>(controller));
    712 
    713   // First load a page that has a favicon that requires
    714   // authentication.  There should be no login prompt.
    715   {
    716     GURL test_page = test_server()->GetURL(kFaviconTestPage);
    717     WindowedLoadStopObserver load_stop_waiter(controller, 1);
    718     browser()->OpenURL(OpenURLParams(
    719         test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    720         false));
    721     load_stop_waiter.Wait();
    722   }
    723 
    724   // Now request the same favicon, but directly as the document.
    725   // There should be one login prompt.
    726   {
    727     GURL test_page = test_server()->GetURL(kFaviconResource);
    728     WindowedLoadStopObserver load_stop_waiter(controller, 1);
    729     WindowedAuthNeededObserver auth_needed_waiter(controller);
    730     browser()->OpenURL(OpenURLParams(
    731         test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    732         false));
    733     auth_needed_waiter.Wait();
    734     ASSERT_EQ(1u, observer.handlers_.size());
    735 
    736     while (!observer.handlers_.empty()) {
    737       WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
    738       LoginHandler* handler = *observer.handlers_.begin();
    739 
    740       ASSERT_TRUE(handler);
    741       handler->CancelAuth();
    742       auth_cancelled_waiter.Wait();
    743     }
    744 
    745     load_stop_waiter.Wait();
    746   }
    747 
    748   EXPECT_EQ(0, observer.auth_supplied_count_);
    749   EXPECT_EQ(1, observer.auth_needed_count_);
    750   EXPECT_EQ(1, observer.auth_cancelled_count_);
    751   EXPECT_TRUE(test_server()->Stop());
    752 }
    753 
    754 // Block crossdomain image login prompting as a phishing defense.
    755 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
    756                        BlockCrossdomainPrompt) {
    757   const char* kTestPage = "files/login/load_img_from_b.html";
    758 
    759   host_resolver()->AddRule("www.a.com", "127.0.0.1");
    760   host_resolver()->AddRule("www.b.com", "127.0.0.1");
    761   ASSERT_TRUE(test_server()->Start());
    762 
    763   content::WebContents* contents =
    764       browser()->tab_strip_model()->GetActiveWebContents();
    765   NavigationController* controller = &contents->GetController();
    766   LoginPromptBrowserTestObserver observer;
    767   observer.Register(content::Source<NavigationController>(controller));
    768 
    769   // Load a page that has a cross-domain sub-resource authentication.
    770   // There should be no login prompt.
    771   {
    772     GURL test_page = test_server()->GetURL(kTestPage);
    773     ASSERT_EQ("127.0.0.1", test_page.host());
    774 
    775     // Change the host from 127.0.0.1 to www.a.com so that when the
    776     // page tries to load from b, it will be cross-origin.
    777     std::string new_host("www.a.com");
    778     GURL::Replacements replacements;
    779     replacements.SetHostStr(new_host);
    780     test_page = test_page.ReplaceComponents(replacements);
    781 
    782     WindowedLoadStopObserver load_stop_waiter(controller, 1);
    783     browser()->OpenURL(OpenURLParams(
    784         test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    785         false));
    786     load_stop_waiter.Wait();
    787   }
    788 
    789   EXPECT_EQ(0, observer.auth_needed_count_);
    790 
    791   // Now request the same page, but from the same origin.
    792   // There should be one login prompt.
    793   {
    794     GURL test_page = test_server()->GetURL(kTestPage);
    795     ASSERT_EQ("127.0.0.1", test_page.host());
    796 
    797     // Change the host from 127.0.0.1 to www.b.com so that when the
    798     // page tries to load from b, it will be same-origin.
    799     std::string new_host("www.b.com");
    800     GURL::Replacements replacements;
    801     replacements.SetHostStr(new_host);
    802     test_page = test_page.ReplaceComponents(replacements);
    803 
    804     WindowedAuthNeededObserver auth_needed_waiter(controller);
    805     browser()->OpenURL(OpenURLParams(
    806         test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    807         false));
    808     auth_needed_waiter.Wait();
    809     ASSERT_EQ(1u, observer.handlers_.size());
    810 
    811     while (!observer.handlers_.empty()) {
    812       WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
    813       LoginHandler* handler = *observer.handlers_.begin();
    814 
    815       ASSERT_TRUE(handler);
    816       handler->CancelAuth();
    817       auth_cancelled_waiter.Wait();
    818     }
    819   }
    820 
    821   EXPECT_EQ(1, observer.auth_needed_count_);
    822   EXPECT_TRUE(test_server()->Stop());
    823 }
    824 
    825 // Allow crossdomain iframe login prompting despite the above.
    826 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
    827                        AllowCrossdomainPrompt) {
    828   const char* kTestPage = "files/login/load_iframe_from_b.html";
    829 
    830   host_resolver()->AddRule("www.a.com", "127.0.0.1");
    831   host_resolver()->AddRule("www.b.com", "127.0.0.1");
    832   ASSERT_TRUE(test_server()->Start());
    833 
    834   content::WebContents* contents =
    835       browser()->tab_strip_model()->GetActiveWebContents();
    836   NavigationController* controller = &contents->GetController();
    837   LoginPromptBrowserTestObserver observer;
    838   observer.Register(content::Source<NavigationController>(controller));
    839 
    840   // Load a page that has a cross-domain iframe authentication.
    841   {
    842     GURL test_page = test_server()->GetURL(kTestPage);
    843     ASSERT_EQ("127.0.0.1", test_page.host());
    844 
    845     // Change the host from 127.0.0.1 to www.a.com so that when the
    846     // page tries to load from b, it will be cross-origin.
    847     std::string new_host("www.a.com");
    848     GURL::Replacements replacements;
    849     replacements.SetHostStr(new_host);
    850     test_page = test_page.ReplaceComponents(replacements);
    851 
    852     WindowedAuthNeededObserver auth_needed_waiter(controller);
    853     browser()->OpenURL(OpenURLParams(
    854         test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
    855         false));
    856     auth_needed_waiter.Wait();
    857     ASSERT_EQ(1u, observer.handlers_.size());
    858 
    859     while (!observer.handlers_.empty()) {
    860       WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
    861       LoginHandler* handler = *observer.handlers_.begin();
    862 
    863       ASSERT_TRUE(handler);
    864       handler->CancelAuth();
    865       auth_cancelled_waiter.Wait();
    866     }
    867   }
    868 
    869   EXPECT_EQ(1, observer.auth_needed_count_);
    870   EXPECT_TRUE(test_server()->Stop());
    871 }
    872 
    873 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, SupplyRedundantAuths) {
    874   ASSERT_TRUE(test_server()->Start());
    875 
    876   // Get NavigationController for tab 1.
    877   content::WebContents* contents_1 =
    878       browser()->tab_strip_model()->GetActiveWebContents();
    879   NavigationController* controller_1 = &contents_1->GetController();
    880 
    881   // Open a new tab.
    882   ui_test_utils::NavigateToURLWithDisposition(
    883       browser(),
    884       GURL("about:blank"),
    885       NEW_FOREGROUND_TAB,
    886       ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
    887 
    888   // Get NavigationController for tab 2.
    889   content::WebContents* contents_2 =
    890       browser()->tab_strip_model()->GetActiveWebContents();
    891   ASSERT_NE(contents_1, contents_2);
    892   NavigationController* controller_2 = &contents_2->GetController();
    893 
    894   LoginPromptBrowserTestObserver observer;
    895   observer.Register(content::Source<NavigationController>(controller_1));
    896   observer.Register(content::Source<NavigationController>(controller_2));
    897 
    898   {
    899     // Open different auth urls in each tab.
    900     WindowedAuthNeededObserver auth_needed_waiter_1(controller_1);
    901     WindowedAuthNeededObserver auth_needed_waiter_2(controller_2);
    902     contents_1->OpenURL(OpenURLParams(
    903         test_server()->GetURL("auth-basic/1"),
    904         content::Referrer(),
    905         CURRENT_TAB,
    906         content::PAGE_TRANSITION_TYPED,
    907         false));
    908     contents_2->OpenURL(OpenURLParams(
    909         test_server()->GetURL("auth-basic/2"),
    910         content::Referrer(),
    911         CURRENT_TAB,
    912         content::PAGE_TRANSITION_TYPED,
    913         false));
    914     auth_needed_waiter_1.Wait();
    915     auth_needed_waiter_2.Wait();
    916 
    917     ASSERT_EQ(2U, observer.handlers_.size());
    918 
    919     // Supply auth in one of the tabs.
    920     WindowedAuthSuppliedObserver auth_supplied_waiter_1(controller_1);
    921     WindowedAuthSuppliedObserver auth_supplied_waiter_2(controller_2);
    922     LoginHandler* handler_1 = *observer.handlers_.begin();
    923     ASSERT_TRUE(handler_1);
    924     SetAuthFor(handler_1);
    925 
    926     // Both tabs should be authenticated.
    927     auth_supplied_waiter_1.Wait();
    928     auth_supplied_waiter_2.Wait();
    929   }
    930 
    931   EXPECT_EQ(2, observer.auth_needed_count_);
    932   EXPECT_EQ(2, observer.auth_supplied_count_);
    933   EXPECT_EQ(0, observer.auth_cancelled_count_);
    934   EXPECT_TRUE(test_server()->Stop());
    935 }
    936 
    937 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, CancelRedundantAuths) {
    938   ASSERT_TRUE(test_server()->Start());
    939 
    940   // Get NavigationController for tab 1.
    941   content::WebContents* contents_1 =
    942       browser()->tab_strip_model()->GetActiveWebContents();
    943   NavigationController* controller_1 = &contents_1->GetController();
    944 
    945   // Open a new tab.
    946   ui_test_utils::NavigateToURLWithDisposition(
    947       browser(),
    948       GURL("about:blank"),
    949       NEW_FOREGROUND_TAB,
    950       ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
    951 
    952   // Get NavigationController for tab 2.
    953   content::WebContents* contents_2 =
    954       browser()->tab_strip_model()->GetActiveWebContents();
    955   ASSERT_NE(contents_1, contents_2);
    956   NavigationController* controller_2 = &contents_2->GetController();
    957 
    958   LoginPromptBrowserTestObserver observer;
    959   observer.Register(content::Source<NavigationController>(controller_1));
    960   observer.Register(content::Source<NavigationController>(controller_2));
    961 
    962   {
    963     // Open different auth urls in each tab.
    964     WindowedAuthNeededObserver auth_needed_waiter_1(controller_1);
    965     WindowedAuthNeededObserver auth_needed_waiter_2(controller_2);
    966     contents_1->OpenURL(OpenURLParams(
    967         test_server()->GetURL("auth-basic/1"),
    968         content::Referrer(),
    969         CURRENT_TAB,
    970         content::PAGE_TRANSITION_TYPED,
    971         false));
    972     contents_2->OpenURL(OpenURLParams(
    973         test_server()->GetURL("auth-basic/2"),
    974         content::Referrer(),
    975         CURRENT_TAB,
    976         content::PAGE_TRANSITION_TYPED,
    977         false));
    978     auth_needed_waiter_1.Wait();
    979     auth_needed_waiter_2.Wait();
    980 
    981     ASSERT_EQ(2U, observer.handlers_.size());
    982 
    983     // Cancel auth in one of the tabs.
    984     WindowedAuthCancelledObserver auth_cancelled_waiter_1(controller_1);
    985     WindowedAuthCancelledObserver auth_cancelled_waiter_2(controller_2);
    986     LoginHandler* handler_1 = *observer.handlers_.begin();
    987     ASSERT_TRUE(handler_1);
    988     handler_1->CancelAuth();
    989 
    990     // Both tabs should cancel auth.
    991     auth_cancelled_waiter_1.Wait();
    992     auth_cancelled_waiter_2.Wait();
    993   }
    994 
    995   EXPECT_EQ(2, observer.auth_needed_count_);
    996   EXPECT_EQ(0, observer.auth_supplied_count_);
    997   EXPECT_EQ(2, observer.auth_cancelled_count_);
    998   EXPECT_TRUE(test_server()->Stop());
    999 }
   1000 
   1001 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
   1002                        SupplyRedundantAuthsMultiProfile) {
   1003   ASSERT_TRUE(test_server()->Start());
   1004 
   1005   // Get NavigationController for regular tab.
   1006   content::WebContents* contents =
   1007       browser()->tab_strip_model()->GetActiveWebContents();
   1008   NavigationController* controller = &contents->GetController();
   1009 
   1010   // Open an incognito window.
   1011   Browser* browser_incognito = CreateIncognitoBrowser();
   1012 
   1013   // Get NavigationController for incognito tab.
   1014   content::WebContents* contents_incognito =
   1015       browser_incognito->tab_strip_model()->GetActiveWebContents();
   1016   ASSERT_NE(contents, contents_incognito);
   1017   NavigationController* controller_incognito =
   1018       &contents_incognito->GetController();
   1019 
   1020   LoginPromptBrowserTestObserver observer;
   1021   observer.Register(content::Source<NavigationController>(controller));
   1022   LoginPromptBrowserTestObserver observer_incognito;
   1023   observer_incognito.Register(
   1024       content::Source<NavigationController>(controller_incognito));
   1025 
   1026   {
   1027     // Open an auth url in each window.
   1028     WindowedAuthNeededObserver auth_needed_waiter(controller);
   1029     WindowedAuthNeededObserver auth_needed_waiter_incognito(
   1030         controller_incognito);
   1031     contents->OpenURL(OpenURLParams(
   1032         test_server()->GetURL("auth-basic/1"),
   1033         content::Referrer(),
   1034         CURRENT_TAB,
   1035         content::PAGE_TRANSITION_TYPED,
   1036         false));
   1037     contents_incognito->OpenURL(OpenURLParams(
   1038         test_server()->GetURL("auth-basic/2"),
   1039         content::Referrer(),
   1040         CURRENT_TAB,
   1041         content::PAGE_TRANSITION_TYPED,
   1042         false));
   1043     auth_needed_waiter.Wait();
   1044     auth_needed_waiter_incognito.Wait();
   1045 
   1046     ASSERT_EQ(1U, observer.handlers_.size());
   1047     ASSERT_EQ(1U, observer_incognito.handlers_.size());
   1048 
   1049     // Supply auth in regular tab.
   1050     WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
   1051     LoginHandler* handler = *observer.handlers_.begin();
   1052     ASSERT_TRUE(handler);
   1053     SetAuthFor(handler);
   1054 
   1055     // Regular tab should be authenticated.
   1056     auth_supplied_waiter.Wait();
   1057 
   1058     // There's not really a way to wait for the incognito window to "do
   1059     // nothing".  Run anything pending in the message loop just to be sure.
   1060     // (This shouldn't be necessary since notifications are synchronous, but
   1061     // maybe it will help avoid flake someday in the future..)
   1062     content::RunAllPendingInMessageLoop();
   1063   }
   1064 
   1065   EXPECT_EQ(1, observer.auth_needed_count_);
   1066   EXPECT_EQ(1, observer.auth_supplied_count_);
   1067   EXPECT_EQ(0, observer.auth_cancelled_count_);
   1068   EXPECT_EQ(1, observer_incognito.auth_needed_count_);
   1069   EXPECT_EQ(0, observer_incognito.auth_supplied_count_);
   1070   EXPECT_EQ(0, observer_incognito.auth_cancelled_count_);
   1071   EXPECT_TRUE(test_server()->Stop());
   1072 }
   1073 
   1074 }  // namespace
   1075