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/login/login_prompt_test_utils.h"
17#include "chrome/browser/ui/tabs/tab_strip_model.h"
18#include "chrome/test/base/in_process_browser_test.h"
19#include "chrome/test/base/ui_test_utils.h"
20#include "content/public/browser/interstitial_page.h"
21#include "content/public/browser/notification_details.h"
22#include "content/public/browser/notification_source.h"
23#include "content/public/browser/web_contents.h"
24#include "content/public/test/browser_test_utils.h"
25#include "content/public/test/test_navigation_observer.h"
26#include "net/base/auth.h"
27#include "net/dns/mock_host_resolver.h"
28#include "net/test/spawned_test_server/spawned_test_server.h"
29
30using content::NavigationController;
31using content::OpenURLParams;
32using content::Referrer;
33
34namespace {
35
36class LoginPromptBrowserTest : public InProcessBrowserTest {
37 public:
38  LoginPromptBrowserTest()
39      : bad_password_("incorrect"),
40        bad_username_("nouser"),
41        password_("secret"),
42        username_basic_("basicuser"),
43        username_digest_("digestuser") {
44    auth_map_["foo"] = AuthInfo("testuser", "foopassword");
45    auth_map_["bar"] = AuthInfo("testuser", "barpassword");
46    auth_map_["testrealm"] = AuthInfo(username_basic_, password_);
47  }
48
49 protected:
50  struct AuthInfo {
51    std::string username_;
52    std::string password_;
53
54    AuthInfo() {}
55
56    AuthInfo(const std::string& username,
57             const std::string& password)
58        : username_(username), password_(password) {}
59  };
60
61  typedef std::map<std::string, AuthInfo> AuthMap;
62
63  void SetAuthFor(LoginHandler* handler);
64
65  void TestCrossOriginPrompt(const GURL& visit_url,
66                             const std::string& landing_host) const;
67
68  AuthMap auth_map_;
69  std::string bad_password_;
70  std::string bad_username_;
71  std::string password_;
72  std::string username_basic_;
73  std::string username_digest_;
74};
75
76void LoginPromptBrowserTest::SetAuthFor(LoginHandler* handler) {
77  const net::AuthChallengeInfo* challenge = handler->auth_info();
78
79  ASSERT_TRUE(challenge);
80  AuthMap::iterator i = auth_map_.find(challenge->realm);
81  EXPECT_TRUE(auth_map_.end() != i);
82  if (i != auth_map_.end()) {
83    const AuthInfo& info = i->second;
84    handler->SetAuth(base::UTF8ToUTF16(info.username_),
85                     base::UTF8ToUTF16(info.password_));
86  }
87}
88
89class InterstitialObserver : public content::WebContentsObserver {
90 public:
91  InterstitialObserver(content::WebContents* web_contents,
92                       const base::Closure& attach_callback,
93                       const base::Closure& detach_callback)
94      : WebContentsObserver(web_contents),
95        attach_callback_(attach_callback),
96        detach_callback_(detach_callback) {
97  }
98
99  virtual void DidAttachInterstitialPage() OVERRIDE {
100    attach_callback_.Run();
101  }
102
103  virtual void DidDetachInterstitialPage() OVERRIDE {
104    detach_callback_.Run();
105  }
106
107 private:
108  base::Closure attach_callback_;
109  base::Closure detach_callback_;
110
111  DISALLOW_COPY_AND_ASSIGN(InterstitialObserver);
112};
113
114void WaitForInterstitialAttach(content::WebContents* web_contents) {
115  scoped_refptr<content::MessageLoopRunner> interstitial_attach_loop_runner(
116      new content::MessageLoopRunner);
117  InterstitialObserver observer(
118      web_contents,
119      interstitial_attach_loop_runner->QuitClosure(),
120      base::Closure());
121  if (!content::InterstitialPage::GetInterstitialPage(web_contents))
122    interstitial_attach_loop_runner->Run();
123}
124
125const char kPrefetchAuthPage[] = "files/login/prefetch.html";
126
127const char kMultiRealmTestPage[] = "files/login/multi_realm.html";
128const int  kMultiRealmTestRealmCount = 2;
129
130const char kSingleRealmTestPage[] = "files/login/single_realm.html";
131
132const char* kAuthBasicPage = "auth-basic";
133const char* kAuthDigestPage = "auth-digest";
134
135base::string16 ExpectedTitleFromAuth(const base::string16& username,
136                                     const base::string16& password) {
137  // The TestServer sets the title to username/password on successful login.
138  return username + base::UTF8ToUTF16("/") + password;
139}
140
141// Confirm that <link rel="prefetch"> targetting an auth required
142// resource does not provide a login dialog.  These types of requests
143// should instead just cancel the auth.
144
145// Unfortunately, this test doesn't assert on anything for its
146// correctness.  Instead, it relies on the auth dialog blocking the
147// browser, and triggering a timeout to cause failure when the
148// prefetch resource requires authorization.
149IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, PrefetchAuthCancels) {
150  ASSERT_TRUE(test_server()->Start());
151
152  GURL test_page = test_server()->GetURL(kPrefetchAuthPage);
153
154  class SetPrefetchForTest {
155   public:
156    explicit SetPrefetchForTest(bool prefetch)
157        : old_prerender_mode_(prerender::PrerenderManager::GetMode()) {
158      std::string exp_group = prefetch ? "ExperimentYes" : "ExperimentNo";
159      base::FieldTrialList::CreateFieldTrial("Prefetch", exp_group);
160      // Disable prerender so this is just a prefetch of the top-level page.
161      prerender::PrerenderManager::SetMode(
162          prerender::PrerenderManager::PRERENDER_MODE_DISABLED);
163    }
164
165    ~SetPrefetchForTest() {
166      prerender::PrerenderManager::SetMode(old_prerender_mode_);
167    }
168
169   private:
170    prerender::PrerenderManager::PrerenderManagerMode old_prerender_mode_;
171  } set_prefetch_for_test(true);
172
173  content::WebContents* contents =
174      browser()->tab_strip_model()->GetActiveWebContents();
175  NavigationController* controller = &contents->GetController();
176  LoginPromptBrowserTestObserver observer;
177
178  observer.Register(content::Source<NavigationController>(controller));
179
180  WindowedLoadStopObserver load_stop_waiter(controller, 1);
181  browser()->OpenURL(OpenURLParams(
182      test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
183      false));
184
185  load_stop_waiter.Wait();
186  EXPECT_TRUE(observer.handlers().empty());
187  EXPECT_TRUE(test_server()->Stop());
188}
189
190// Test that "Basic" HTTP authentication works.
191IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestBasicAuth) {
192  ASSERT_TRUE(test_server()->Start());
193  GURL test_page = test_server()->GetURL(kAuthBasicPage);
194
195  content::WebContents* contents =
196      browser()->tab_strip_model()->GetActiveWebContents();
197  NavigationController* controller = &contents->GetController();
198  LoginPromptBrowserTestObserver observer;
199
200  observer.Register(content::Source<NavigationController>(controller));
201
202  {
203    WindowedAuthNeededObserver auth_needed_waiter(controller);
204    browser()->OpenURL(OpenURLParams(
205        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
206        false));
207    auth_needed_waiter.Wait();
208  }
209
210  ASSERT_FALSE(observer.handlers().empty());
211  {
212    WindowedAuthNeededObserver auth_needed_waiter(controller);
213    WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
214    LoginHandler* handler = *observer.handlers().begin();
215
216    ASSERT_TRUE(handler);
217    handler->SetAuth(base::UTF8ToUTF16(bad_username_),
218                     base::UTF8ToUTF16(bad_password_));
219    auth_supplied_waiter.Wait();
220
221    // The request should be retried after the incorrect password is
222    // supplied.  This should result in a new AUTH_NEEDED notification
223    // for the same realm.
224    auth_needed_waiter.Wait();
225  }
226
227  ASSERT_EQ(1u, observer.handlers().size());
228  WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
229  LoginHandler* handler = *observer.handlers().begin();
230  SetAuthFor(handler);
231  auth_supplied_waiter.Wait();
232
233  base::string16 expected_title =
234      ExpectedTitleFromAuth(base::ASCIIToUTF16("basicuser"),
235                            base::ASCIIToUTF16("secret"));
236  content::TitleWatcher title_watcher(contents, expected_title);
237  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
238}
239
240// Test that "Digest" HTTP authentication works.
241IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestDigestAuth) {
242  ASSERT_TRUE(test_server()->Start());
243  GURL test_page = test_server()->GetURL(kAuthDigestPage);
244
245  content::WebContents* contents =
246      browser()->tab_strip_model()->GetActiveWebContents();
247  NavigationController* controller = &contents->GetController();
248  LoginPromptBrowserTestObserver observer;
249
250  observer.Register(content::Source<NavigationController>(controller));
251
252  {
253    WindowedAuthNeededObserver auth_needed_waiter(controller);
254    browser()->OpenURL(OpenURLParams(
255        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
256        false));
257    auth_needed_waiter.Wait();
258  }
259
260  ASSERT_FALSE(observer.handlers().empty());
261  {
262    WindowedAuthNeededObserver auth_needed_waiter(controller);
263    WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
264    LoginHandler* handler = *observer.handlers().begin();
265
266    ASSERT_TRUE(handler);
267    handler->SetAuth(base::UTF8ToUTF16(bad_username_),
268                     base::UTF8ToUTF16(bad_password_));
269    auth_supplied_waiter.Wait();
270
271    // The request should be retried after the incorrect password is
272    // supplied.  This should result in a new AUTH_NEEDED notification
273    // for the same realm.
274    auth_needed_waiter.Wait();
275  }
276
277  ASSERT_EQ(1u, observer.handlers().size());
278  WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
279  LoginHandler* handler = *observer.handlers().begin();
280
281  base::string16 username(base::UTF8ToUTF16(username_digest_));
282  base::string16 password(base::UTF8ToUTF16(password_));
283  handler->SetAuth(username, password);
284  auth_supplied_waiter.Wait();
285
286  base::string16 expected_title = ExpectedTitleFromAuth(username, password);
287  content::TitleWatcher title_watcher(contents, expected_title);
288  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
289}
290
291IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestTwoAuths) {
292  ASSERT_TRUE(test_server()->Start());
293
294  content::WebContents* contents1 =
295      browser()->tab_strip_model()->GetActiveWebContents();
296  NavigationController* controller1 = &contents1->GetController();
297  LoginPromptBrowserTestObserver observer;
298
299  observer.Register(content::Source<NavigationController>(controller1));
300
301  // Open a new tab.
302  ui_test_utils::NavigateToURLWithDisposition(
303      browser(),
304      GURL("about:blank"),
305      NEW_FOREGROUND_TAB,
306      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
307
308  content::WebContents* contents2 =
309      browser()->tab_strip_model()->GetActiveWebContents();
310  ASSERT_NE(contents1, contents2);
311  NavigationController* controller2 = &contents2->GetController();
312  observer.Register(content::Source<NavigationController>(controller2));
313
314  {
315    WindowedAuthNeededObserver auth_needed_waiter(controller1);
316    contents1->OpenURL(OpenURLParams(
317        test_server()->GetURL(kAuthBasicPage), Referrer(),
318        CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false));
319    auth_needed_waiter.Wait();
320  }
321
322  {
323    WindowedAuthNeededObserver auth_needed_waiter(controller2);
324    contents2->OpenURL(OpenURLParams(
325        test_server()->GetURL(kAuthDigestPage), Referrer(),
326        CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false));
327    auth_needed_waiter.Wait();
328  }
329
330  ASSERT_EQ(2u, observer.handlers().size());
331
332  LoginHandler* handler1 = *observer.handlers().begin();
333  LoginHandler* handler2 = *(++(observer.handlers().begin()));
334
335  base::string16 expected_title1 = ExpectedTitleFromAuth(
336      base::UTF8ToUTF16(username_basic_), base::UTF8ToUTF16(password_));
337  base::string16 expected_title2 = ExpectedTitleFromAuth(
338      base::UTF8ToUTF16(username_digest_), base::UTF8ToUTF16(password_));
339  content::TitleWatcher title_watcher1(contents1, expected_title1);
340  content::TitleWatcher title_watcher2(contents2, expected_title2);
341
342  handler1->SetAuth(base::UTF8ToUTF16(username_basic_),
343                    base::UTF8ToUTF16(password_));
344  handler2->SetAuth(base::UTF8ToUTF16(username_digest_),
345                    base::UTF8ToUTF16(password_));
346
347  EXPECT_EQ(expected_title1, title_watcher1.WaitAndGetTitle());
348  EXPECT_EQ(expected_title2, title_watcher2.WaitAndGetTitle());
349}
350
351// Test login prompt cancellation.
352IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestCancelAuth) {
353  ASSERT_TRUE(test_server()->Start());
354  GURL auth_page = test_server()->GetURL(kAuthBasicPage);
355  GURL no_auth_page_1 = test_server()->GetURL("a");
356  GURL no_auth_page_2 = test_server()->GetURL("b");
357  GURL no_auth_page_3 = test_server()->GetURL("c");
358
359  content::WebContents* contents =
360      browser()->tab_strip_model()->GetActiveWebContents();
361  NavigationController* controller = &contents->GetController();
362
363  LoginPromptBrowserTestObserver observer;
364  observer.Register(content::Source<NavigationController>(controller));
365
366  // First navigate to an unauthenticated page so we have something to
367  // go back to.
368  ui_test_utils::NavigateToURL(browser(), no_auth_page_1);
369
370  // Navigating while auth is requested is the same as cancelling.
371  {
372    // We need to wait for two LOAD_STOP events.  One for auth_page and one for
373    // no_auth_page_2.
374    WindowedLoadStopObserver load_stop_waiter(controller, 2);
375    WindowedAuthNeededObserver auth_needed_waiter(controller);
376    browser()->OpenURL(OpenURLParams(
377        auth_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
378        false));
379    auth_needed_waiter.Wait();
380    WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
381    browser()->OpenURL(OpenURLParams(
382        no_auth_page_2, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
383        false));
384    auth_cancelled_waiter.Wait();
385    load_stop_waiter.Wait();
386    EXPECT_TRUE(observer.handlers().empty());
387  }
388
389  // Try navigating backwards.
390  {
391    // As above, we wait for two LOAD_STOP events; one for each navigation.
392    WindowedLoadStopObserver load_stop_waiter(controller, 2);
393    WindowedAuthNeededObserver auth_needed_waiter(controller);
394    browser()->OpenURL(OpenURLParams(
395        auth_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
396        false));
397    auth_needed_waiter.Wait();
398    WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
399    ASSERT_TRUE(chrome::CanGoBack(browser()));
400    chrome::GoBack(browser(), CURRENT_TAB);
401    auth_cancelled_waiter.Wait();
402    load_stop_waiter.Wait();
403    EXPECT_TRUE(observer.handlers().empty());
404  }
405
406  // Now add a page and go back, so we have something to go forward to.
407  ui_test_utils::NavigateToURL(browser(), no_auth_page_3);
408  {
409    WindowedLoadStopObserver load_stop_waiter(controller, 1);
410    chrome::GoBack(browser(), CURRENT_TAB);  // Should take us to page 1
411    load_stop_waiter.Wait();
412  }
413
414  {
415    // We wait for two LOAD_STOP events; one for each navigation.
416    WindowedLoadStopObserver load_stop_waiter(controller, 2);
417    WindowedAuthNeededObserver auth_needed_waiter(controller);
418    browser()->OpenURL(OpenURLParams(
419        auth_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
420        false));
421    auth_needed_waiter.Wait();
422    WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
423    ASSERT_TRUE(chrome::CanGoForward(browser()));
424    chrome::GoForward(browser(), CURRENT_TAB);  // Should take us to page 3
425    auth_cancelled_waiter.Wait();
426    load_stop_waiter.Wait();
427    EXPECT_TRUE(observer.handlers().empty());
428  }
429
430  // Now test that cancelling works as expected.
431  {
432    WindowedLoadStopObserver load_stop_waiter(controller, 1);
433    WindowedAuthNeededObserver auth_needed_waiter(controller);
434    browser()->OpenURL(OpenURLParams(
435        auth_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
436        false));
437    auth_needed_waiter.Wait();
438    WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
439    LoginHandler* handler = *observer.handlers().begin();
440    ASSERT_TRUE(handler);
441    handler->CancelAuth();
442    auth_cancelled_waiter.Wait();
443    load_stop_waiter.Wait();
444    EXPECT_TRUE(observer.handlers().empty());
445  }
446}
447
448// Test handling of resources that require authentication even though
449// the page they are included on doesn't.  In this case we should only
450// present the minimal number of prompts necessary for successfully
451// displaying the page.  First we check whether cancelling works as
452// expected.
453IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmCancellation) {
454  ASSERT_TRUE(test_server()->Start());
455  GURL test_page = test_server()->GetURL(kMultiRealmTestPage);
456
457  content::WebContents* contents =
458      browser()->tab_strip_model()->GetActiveWebContents();
459  NavigationController* controller = &contents->GetController();
460  LoginPromptBrowserTestObserver observer;
461
462  observer.Register(content::Source<NavigationController>(controller));
463
464  WindowedLoadStopObserver load_stop_waiter(controller, 1);
465
466  {
467    WindowedAuthNeededObserver auth_needed_waiter(controller);
468    browser()->OpenURL(OpenURLParams(
469        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
470        false));
471    auth_needed_waiter.Wait();
472  }
473
474  int n_handlers = 0;
475
476  while (n_handlers < kMultiRealmTestRealmCount) {
477    WindowedAuthNeededObserver auth_needed_waiter(controller);
478
479    while (!observer.handlers().empty()) {
480      WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
481      LoginHandler* handler = *observer.handlers().begin();
482
483      ASSERT_TRUE(handler);
484      n_handlers++;
485      handler->CancelAuth();
486      auth_cancelled_waiter.Wait();
487    }
488
489    if (n_handlers < kMultiRealmTestRealmCount)
490      auth_needed_waiter.Wait();
491  }
492
493  load_stop_waiter.Wait();
494
495  EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers);
496  EXPECT_EQ(0, observer.auth_supplied_count());
497  EXPECT_LT(0, observer.auth_needed_count());
498  EXPECT_LT(0, observer.auth_cancelled_count());
499  EXPECT_TRUE(test_server()->Stop());
500}
501
502// Similar to the MultipleRealmCancellation test above, but tests
503// whether supplying credentials work as exepcted.
504IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmConfirmation) {
505  ASSERT_TRUE(test_server()->Start());
506  GURL test_page = test_server()->GetURL(kMultiRealmTestPage);
507
508  content::WebContents* contents =
509      browser()->tab_strip_model()->GetActiveWebContents();
510  NavigationController* controller = &contents->GetController();
511  LoginPromptBrowserTestObserver observer;
512
513  observer.Register(content::Source<NavigationController>(controller));
514
515  WindowedLoadStopObserver load_stop_waiter(controller, 1);
516  int n_handlers = 0;
517
518  {
519    WindowedAuthNeededObserver auth_needed_waiter(controller);
520
521    browser()->OpenURL(OpenURLParams(
522        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
523        false));
524    auth_needed_waiter.Wait();
525  }
526
527  while (n_handlers < kMultiRealmTestRealmCount) {
528    WindowedAuthNeededObserver auth_needed_waiter(controller);
529
530    while (!observer.handlers().empty()) {
531      WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
532      LoginHandler* handler = *observer.handlers().begin();
533
534      ASSERT_TRUE(handler);
535      n_handlers++;
536      SetAuthFor(handler);
537      auth_supplied_waiter.Wait();
538    }
539
540    if (n_handlers < kMultiRealmTestRealmCount)
541      auth_needed_waiter.Wait();
542  }
543
544  load_stop_waiter.Wait();
545
546  EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers);
547  EXPECT_LT(0, observer.auth_needed_count());
548  EXPECT_LT(0, observer.auth_supplied_count());
549  EXPECT_EQ(0, observer.auth_cancelled_count());
550  EXPECT_TRUE(test_server()->Stop());
551}
552
553// Testing for recovery from an incorrect password for the case where
554// there are multiple authenticated resources.
555IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, IncorrectConfirmation) {
556  ASSERT_TRUE(test_server()->Start());
557  GURL test_page = test_server()->GetURL(kSingleRealmTestPage);
558
559  content::WebContents* contents =
560      browser()->tab_strip_model()->GetActiveWebContents();
561  NavigationController* controller = &contents->GetController();
562  LoginPromptBrowserTestObserver observer;
563
564  observer.Register(content::Source<NavigationController>(controller));
565
566  {
567    WindowedAuthNeededObserver auth_needed_waiter(controller);
568    browser()->OpenURL(OpenURLParams(
569        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
570        false));
571    auth_needed_waiter.Wait();
572  }
573
574  EXPECT_FALSE(observer.handlers().empty());
575
576  if (!observer.handlers().empty()) {
577    WindowedAuthNeededObserver auth_needed_waiter(controller);
578    WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
579    LoginHandler* handler = *observer.handlers().begin();
580
581    ASSERT_TRUE(handler);
582    handler->SetAuth(base::UTF8ToUTF16(bad_username_),
583                     base::UTF8ToUTF16(bad_password_));
584    auth_supplied_waiter.Wait();
585
586    // The request should be retried after the incorrect password is
587    // supplied.  This should result in a new AUTH_NEEDED notification
588    // for the same realm.
589    auth_needed_waiter.Wait();
590  }
591
592  int n_handlers = 0;
593
594  while (n_handlers < 1) {
595    WindowedAuthNeededObserver auth_needed_waiter(controller);
596
597    while (!observer.handlers().empty()) {
598      WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
599      LoginHandler* handler = *observer.handlers().begin();
600
601      ASSERT_TRUE(handler);
602      n_handlers++;
603      SetAuthFor(handler);
604      auth_supplied_waiter.Wait();
605    }
606
607    if (n_handlers < 1)
608      auth_needed_waiter.Wait();
609  }
610
611  // The single realm test has only one realm, and thus only one login
612  // prompt.
613  EXPECT_EQ(1, n_handlers);
614  EXPECT_LT(0, observer.auth_needed_count());
615  EXPECT_EQ(0, observer.auth_cancelled_count());
616  EXPECT_EQ(observer.auth_needed_count(), observer.auth_supplied_count());
617  EXPECT_TRUE(test_server()->Stop());
618}
619
620// If the favicon is an authenticated resource, we shouldn't prompt
621// for credentials.  The same URL, if requested elsewhere should
622// prompt for credentials.
623IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, NoLoginPromptForFavicon) {
624  const char* kFaviconTestPage = "files/login/has_favicon.html";
625  const char* kFaviconResource = "auth-basic/favicon.gif";
626
627  ASSERT_TRUE(test_server()->Start());
628
629  content::WebContents* contents =
630      browser()->tab_strip_model()->GetActiveWebContents();
631  NavigationController* controller = &contents->GetController();
632  LoginPromptBrowserTestObserver observer;
633
634  observer.Register(content::Source<NavigationController>(controller));
635
636  // First load a page that has a favicon that requires
637  // authentication.  There should be no login prompt.
638  {
639    GURL test_page = test_server()->GetURL(kFaviconTestPage);
640    WindowedLoadStopObserver load_stop_waiter(controller, 1);
641    browser()->OpenURL(OpenURLParams(
642        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
643        false));
644    load_stop_waiter.Wait();
645  }
646
647  // Now request the same favicon, but directly as the document.
648  // There should be one login prompt.
649  {
650    GURL test_page = test_server()->GetURL(kFaviconResource);
651    WindowedLoadStopObserver load_stop_waiter(controller, 1);
652    WindowedAuthNeededObserver auth_needed_waiter(controller);
653    browser()->OpenURL(OpenURLParams(
654        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
655        false));
656    auth_needed_waiter.Wait();
657    ASSERT_EQ(1u, observer.handlers().size());
658
659    while (!observer.handlers().empty()) {
660      WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
661      LoginHandler* handler = *observer.handlers().begin();
662
663      ASSERT_TRUE(handler);
664      handler->CancelAuth();
665      auth_cancelled_waiter.Wait();
666    }
667
668    load_stop_waiter.Wait();
669  }
670
671  EXPECT_EQ(0, observer.auth_supplied_count());
672  EXPECT_EQ(1, observer.auth_needed_count());
673  EXPECT_EQ(1, observer.auth_cancelled_count());
674  EXPECT_TRUE(test_server()->Stop());
675}
676
677// Block crossdomain image login prompting as a phishing defense.
678IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
679                       BlockCrossdomainPromptForSubresources) {
680  const char* kTestPage = "files/login/load_img_from_b.html";
681
682  host_resolver()->AddRule("www.a.com", "127.0.0.1");
683  host_resolver()->AddRule("www.b.com", "127.0.0.1");
684  ASSERT_TRUE(test_server()->Start());
685
686  content::WebContents* contents =
687      browser()->tab_strip_model()->GetActiveWebContents();
688  NavigationController* controller = &contents->GetController();
689  LoginPromptBrowserTestObserver observer;
690  observer.Register(content::Source<NavigationController>(controller));
691
692  // Load a page that has a cross-domain sub-resource authentication.
693  // There should be no login prompt.
694  {
695    GURL test_page = test_server()->GetURL(kTestPage);
696    ASSERT_EQ("127.0.0.1", test_page.host());
697
698    // Change the host from 127.0.0.1 to www.a.com so that when the
699    // page tries to load from b, it will be cross-origin.
700    std::string new_host("www.a.com");
701    GURL::Replacements replacements;
702    replacements.SetHostStr(new_host);
703    test_page = test_page.ReplaceComponents(replacements);
704
705    WindowedLoadStopObserver load_stop_waiter(controller, 1);
706    browser()->OpenURL(OpenURLParams(
707        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
708        false));
709    load_stop_waiter.Wait();
710  }
711
712  EXPECT_EQ(0, observer.auth_needed_count());
713
714  // Now request the same page, but from the same origin.
715  // There should be one login prompt.
716  {
717    GURL test_page = test_server()->GetURL(kTestPage);
718    ASSERT_EQ("127.0.0.1", test_page.host());
719
720    // Change the host from 127.0.0.1 to www.b.com so that when the
721    // page tries to load from b, it will be same-origin.
722    std::string new_host("www.b.com");
723    GURL::Replacements replacements;
724    replacements.SetHostStr(new_host);
725    test_page = test_page.ReplaceComponents(replacements);
726
727    WindowedAuthNeededObserver auth_needed_waiter(controller);
728    browser()->OpenURL(OpenURLParams(
729        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
730        false));
731    auth_needed_waiter.Wait();
732    ASSERT_EQ(1u, observer.handlers().size());
733
734    while (!observer.handlers().empty()) {
735      WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
736      LoginHandler* handler = *observer.handlers().begin();
737
738      ASSERT_TRUE(handler);
739      handler->CancelAuth();
740      auth_cancelled_waiter.Wait();
741    }
742  }
743
744  EXPECT_EQ(1, observer.auth_needed_count());
745  EXPECT_TRUE(test_server()->Stop());
746}
747
748// Allow crossdomain iframe login prompting despite the above.
749IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
750                       AllowCrossdomainPromptForSubframes) {
751  const char* kTestPage = "files/login/load_iframe_from_b.html";
752
753  host_resolver()->AddRule("www.a.com", "127.0.0.1");
754  host_resolver()->AddRule("www.b.com", "127.0.0.1");
755  ASSERT_TRUE(test_server()->Start());
756
757  content::WebContents* contents =
758      browser()->tab_strip_model()->GetActiveWebContents();
759  NavigationController* controller = &contents->GetController();
760  LoginPromptBrowserTestObserver observer;
761  observer.Register(content::Source<NavigationController>(controller));
762
763  // Load a page that has a cross-domain iframe authentication.
764  {
765    GURL test_page = test_server()->GetURL(kTestPage);
766    ASSERT_EQ("127.0.0.1", test_page.host());
767
768    // Change the host from 127.0.0.1 to www.a.com so that when the
769    // page tries to load from b, it will be cross-origin.
770    std::string new_host("www.a.com");
771    GURL::Replacements replacements;
772    replacements.SetHostStr(new_host);
773    test_page = test_page.ReplaceComponents(replacements);
774
775    WindowedAuthNeededObserver auth_needed_waiter(controller);
776    browser()->OpenURL(OpenURLParams(
777        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
778        false));
779    auth_needed_waiter.Wait();
780    ASSERT_EQ(1u, observer.handlers().size());
781
782    while (!observer.handlers().empty()) {
783      WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
784      LoginHandler* handler = *observer.handlers().begin();
785
786      ASSERT_TRUE(handler);
787      // When a cross origin iframe displays a login prompt, the blank
788      // interstitial shouldn't be displayed and the omnibox should show the
789      // main frame's url, not the iframe's.
790      EXPECT_EQ(new_host, contents->GetVisibleURL().host());
791
792      handler->CancelAuth();
793      auth_cancelled_waiter.Wait();
794    }
795  }
796
797  // Should stay on the main frame's url once the prompt the iframe is closed.
798  EXPECT_EQ("www.a.com", contents->GetVisibleURL().host());
799
800  EXPECT_EQ(1, observer.auth_needed_count());
801  EXPECT_TRUE(test_server()->Stop());
802}
803
804IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, SupplyRedundantAuths) {
805  ASSERT_TRUE(test_server()->Start());
806
807  // Get NavigationController for tab 1.
808  content::WebContents* contents_1 =
809      browser()->tab_strip_model()->GetActiveWebContents();
810  NavigationController* controller_1 = &contents_1->GetController();
811
812  // Open a new tab.
813  ui_test_utils::NavigateToURLWithDisposition(
814      browser(),
815      GURL("about:blank"),
816      NEW_FOREGROUND_TAB,
817      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
818
819  // Get NavigationController for tab 2.
820  content::WebContents* contents_2 =
821      browser()->tab_strip_model()->GetActiveWebContents();
822  ASSERT_NE(contents_1, contents_2);
823  NavigationController* controller_2 = &contents_2->GetController();
824
825  LoginPromptBrowserTestObserver observer;
826  observer.Register(content::Source<NavigationController>(controller_1));
827  observer.Register(content::Source<NavigationController>(controller_2));
828
829  {
830    // Open different auth urls in each tab.
831    WindowedAuthNeededObserver auth_needed_waiter_1(controller_1);
832    WindowedAuthNeededObserver auth_needed_waiter_2(controller_2);
833    contents_1->OpenURL(OpenURLParams(
834        test_server()->GetURL("auth-basic/1"),
835        content::Referrer(),
836        CURRENT_TAB,
837        ui::PAGE_TRANSITION_TYPED,
838        false));
839    contents_2->OpenURL(OpenURLParams(
840        test_server()->GetURL("auth-basic/2"),
841        content::Referrer(),
842        CURRENT_TAB,
843        ui::PAGE_TRANSITION_TYPED,
844        false));
845    auth_needed_waiter_1.Wait();
846    auth_needed_waiter_2.Wait();
847
848    ASSERT_EQ(2U, observer.handlers().size());
849
850    // Supply auth in one of the tabs.
851    WindowedAuthSuppliedObserver auth_supplied_waiter_1(controller_1);
852    WindowedAuthSuppliedObserver auth_supplied_waiter_2(controller_2);
853    LoginHandler* handler_1 = *observer.handlers().begin();
854    ASSERT_TRUE(handler_1);
855    SetAuthFor(handler_1);
856
857    // Both tabs should be authenticated.
858    auth_supplied_waiter_1.Wait();
859    auth_supplied_waiter_2.Wait();
860  }
861
862  EXPECT_EQ(2, observer.auth_needed_count());
863  EXPECT_EQ(2, observer.auth_supplied_count());
864  EXPECT_EQ(0, observer.auth_cancelled_count());
865  EXPECT_TRUE(test_server()->Stop());
866}
867
868IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, CancelRedundantAuths) {
869  ASSERT_TRUE(test_server()->Start());
870
871  // Get NavigationController for tab 1.
872  content::WebContents* contents_1 =
873      browser()->tab_strip_model()->GetActiveWebContents();
874  NavigationController* controller_1 = &contents_1->GetController();
875
876  // Open a new tab.
877  ui_test_utils::NavigateToURLWithDisposition(
878      browser(),
879      GURL("about:blank"),
880      NEW_FOREGROUND_TAB,
881      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
882
883  // Get NavigationController for tab 2.
884  content::WebContents* contents_2 =
885      browser()->tab_strip_model()->GetActiveWebContents();
886  ASSERT_NE(contents_1, contents_2);
887  NavigationController* controller_2 = &contents_2->GetController();
888
889  LoginPromptBrowserTestObserver observer;
890  observer.Register(content::Source<NavigationController>(controller_1));
891  observer.Register(content::Source<NavigationController>(controller_2));
892
893  {
894    // Open different auth urls in each tab.
895    WindowedAuthNeededObserver auth_needed_waiter_1(controller_1);
896    WindowedAuthNeededObserver auth_needed_waiter_2(controller_2);
897    contents_1->OpenURL(OpenURLParams(
898        test_server()->GetURL("auth-basic/1"),
899        content::Referrer(),
900        CURRENT_TAB,
901        ui::PAGE_TRANSITION_TYPED,
902        false));
903    contents_2->OpenURL(OpenURLParams(
904        test_server()->GetURL("auth-basic/2"),
905        content::Referrer(),
906        CURRENT_TAB,
907        ui::PAGE_TRANSITION_TYPED,
908        false));
909    auth_needed_waiter_1.Wait();
910    auth_needed_waiter_2.Wait();
911
912    ASSERT_EQ(2U, observer.handlers().size());
913
914    // Cancel auth in one of the tabs.
915    WindowedAuthCancelledObserver auth_cancelled_waiter_1(controller_1);
916    WindowedAuthCancelledObserver auth_cancelled_waiter_2(controller_2);
917    LoginHandler* handler_1 = *observer.handlers().begin();
918    ASSERT_TRUE(handler_1);
919    handler_1->CancelAuth();
920
921    // Both tabs should cancel auth.
922    auth_cancelled_waiter_1.Wait();
923    auth_cancelled_waiter_2.Wait();
924  }
925
926  EXPECT_EQ(2, observer.auth_needed_count());
927  EXPECT_EQ(0, observer.auth_supplied_count());
928  EXPECT_EQ(2, observer.auth_cancelled_count());
929  EXPECT_TRUE(test_server()->Stop());
930}
931
932IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
933                       SupplyRedundantAuthsMultiProfile) {
934  ASSERT_TRUE(test_server()->Start());
935
936  // Get NavigationController for regular tab.
937  content::WebContents* contents =
938      browser()->tab_strip_model()->GetActiveWebContents();
939  NavigationController* controller = &contents->GetController();
940
941  // Open an incognito window.
942  Browser* browser_incognito = CreateIncognitoBrowser();
943
944  // Get NavigationController for incognito tab.
945  content::WebContents* contents_incognito =
946      browser_incognito->tab_strip_model()->GetActiveWebContents();
947  ASSERT_NE(contents, contents_incognito);
948  NavigationController* controller_incognito =
949      &contents_incognito->GetController();
950
951  LoginPromptBrowserTestObserver observer;
952  observer.Register(content::Source<NavigationController>(controller));
953  LoginPromptBrowserTestObserver observer_incognito;
954  observer_incognito.Register(
955      content::Source<NavigationController>(controller_incognito));
956
957  {
958    // Open an auth url in each window.
959    WindowedAuthNeededObserver auth_needed_waiter(controller);
960    WindowedAuthNeededObserver auth_needed_waiter_incognito(
961        controller_incognito);
962    contents->OpenURL(OpenURLParams(
963        test_server()->GetURL("auth-basic/1"),
964        content::Referrer(),
965        CURRENT_TAB,
966        ui::PAGE_TRANSITION_TYPED,
967        false));
968    contents_incognito->OpenURL(OpenURLParams(
969        test_server()->GetURL("auth-basic/2"),
970        content::Referrer(),
971        CURRENT_TAB,
972        ui::PAGE_TRANSITION_TYPED,
973        false));
974    auth_needed_waiter.Wait();
975    auth_needed_waiter_incognito.Wait();
976
977    ASSERT_EQ(1U, observer.handlers().size());
978    ASSERT_EQ(1U, observer_incognito.handlers().size());
979
980    // Supply auth in regular tab.
981    WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
982    LoginHandler* handler = *observer.handlers().begin();
983    ASSERT_TRUE(handler);
984    SetAuthFor(handler);
985
986    // Regular tab should be authenticated.
987    auth_supplied_waiter.Wait();
988
989    // There's not really a way to wait for the incognito window to "do
990    // nothing".  Run anything pending in the message loop just to be sure.
991    // (This shouldn't be necessary since notifications are synchronous, but
992    // maybe it will help avoid flake someday in the future..)
993    content::RunAllPendingInMessageLoop();
994  }
995
996  EXPECT_EQ(1, observer.auth_needed_count());
997  EXPECT_EQ(1, observer.auth_supplied_count());
998  EXPECT_EQ(0, observer.auth_cancelled_count());
999  EXPECT_EQ(1, observer_incognito.auth_needed_count());
1000  EXPECT_EQ(0, observer_incognito.auth_supplied_count());
1001  EXPECT_EQ(0, observer_incognito.auth_cancelled_count());
1002  EXPECT_TRUE(test_server()->Stop());
1003}
1004
1005// If an XMLHttpRequest is made with incorrect credentials, there should be no
1006// login prompt; instead the 401 status should be returned to the script.
1007IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
1008                       NoLoginPromptForXHRWithBadCredentials) {
1009  const char* kXHRTestPage = "files/login/xhr_with_credentials.html#incorrect";
1010
1011  ASSERT_TRUE(test_server()->Start());
1012
1013  content::WebContents* contents =
1014      browser()->tab_strip_model()->GetActiveWebContents();
1015  NavigationController* controller = &contents->GetController();
1016  LoginPromptBrowserTestObserver observer;
1017
1018  observer.Register(content::Source<NavigationController>(controller));
1019
1020  // Load a page which makes a synchronous XMLHttpRequest for an authenticated
1021  // resource with the wrong credentials.  There should be no login prompt.
1022  {
1023    GURL test_page = test_server()->GetURL(kXHRTestPage);
1024    WindowedLoadStopObserver load_stop_waiter(controller, 1);
1025    browser()->OpenURL(OpenURLParams(
1026        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
1027        false));
1028    load_stop_waiter.Wait();
1029  }
1030
1031  base::string16 expected_title(base::UTF8ToUTF16("status=401"));
1032
1033  EXPECT_EQ(expected_title, contents->GetTitle());
1034  EXPECT_EQ(0, observer.auth_supplied_count());
1035  EXPECT_EQ(0, observer.auth_needed_count());
1036  EXPECT_EQ(0, observer.auth_cancelled_count());
1037  EXPECT_TRUE(test_server()->Stop());
1038}
1039
1040// If an XMLHttpRequest is made with correct credentials, there should be no
1041// login prompt either.
1042IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
1043                       NoLoginPromptForXHRWithGoodCredentials) {
1044  const char* kXHRTestPage = "files/login/xhr_with_credentials.html#secret";
1045
1046  ASSERT_TRUE(test_server()->Start());
1047
1048  content::WebContents* contents =
1049      browser()->tab_strip_model()->GetActiveWebContents();
1050  NavigationController* controller = &contents->GetController();
1051  LoginPromptBrowserTestObserver observer;
1052
1053  observer.Register(content::Source<NavigationController>(controller));
1054
1055  // Load a page which makes a synchronous XMLHttpRequest for an authenticated
1056  // resource with the wrong credentials.  There should be no login prompt.
1057  {
1058    GURL test_page = test_server()->GetURL(kXHRTestPage);
1059    WindowedLoadStopObserver load_stop_waiter(controller, 1);
1060    browser()->OpenURL(OpenURLParams(
1061        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
1062        false));
1063    load_stop_waiter.Wait();
1064  }
1065
1066  base::string16 expected_title(base::UTF8ToUTF16("status=200"));
1067
1068  EXPECT_EQ(expected_title, contents->GetTitle());
1069  EXPECT_EQ(0, observer.auth_supplied_count());
1070  EXPECT_EQ(0, observer.auth_needed_count());
1071  EXPECT_EQ(0, observer.auth_cancelled_count());
1072  EXPECT_TRUE(test_server()->Stop());
1073}
1074
1075// If an XMLHttpRequest is made without credentials, there should be a login
1076// prompt.
1077IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
1078                       LoginPromptForXHRWithoutCredentials) {
1079  const char* kXHRTestPage = "files/login/xhr_without_credentials.html";
1080
1081  ASSERT_TRUE(test_server()->Start());
1082
1083  content::WebContents* contents =
1084      browser()->tab_strip_model()->GetActiveWebContents();
1085  NavigationController* controller = &contents->GetController();
1086  LoginPromptBrowserTestObserver observer;
1087
1088  observer.Register(content::Source<NavigationController>(controller));
1089
1090  // Load a page which makes a synchronous XMLHttpRequest for an authenticated
1091  // resource with the wrong credentials.  There should be no login prompt.
1092  {
1093    GURL test_page = test_server()->GetURL(kXHRTestPage);
1094    WindowedAuthNeededObserver auth_needed_waiter(controller);
1095    browser()->OpenURL(OpenURLParams(
1096        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
1097        false));
1098    auth_needed_waiter.Wait();
1099  }
1100
1101  ASSERT_FALSE(observer.handlers().empty());
1102  {
1103    WindowedAuthNeededObserver auth_needed_waiter(controller);
1104    WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
1105    LoginHandler* handler = *observer.handlers().begin();
1106
1107    ASSERT_TRUE(handler);
1108    handler->SetAuth(base::UTF8ToUTF16(bad_username_),
1109                     base::UTF8ToUTF16(bad_password_));
1110    auth_supplied_waiter.Wait();
1111
1112    // The request should be retried after the incorrect password is
1113    // supplied.  This should result in a new AUTH_NEEDED notification
1114    // for the same realm.
1115    auth_needed_waiter.Wait();
1116  }
1117
1118  ASSERT_EQ(1u, observer.handlers().size());
1119  WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
1120  LoginHandler* handler = *observer.handlers().begin();
1121
1122  base::string16 username(base::UTF8ToUTF16(username_digest_));
1123  base::string16 password(base::UTF8ToUTF16(password_));
1124  handler->SetAuth(username, password);
1125  auth_supplied_waiter.Wait();
1126
1127  WindowedLoadStopObserver load_stop_waiter(controller, 1);
1128  load_stop_waiter.Wait();
1129
1130  base::string16 expected_title(base::UTF8ToUTF16("status=200"));
1131
1132  EXPECT_EQ(expected_title, contents->GetTitle());
1133  EXPECT_EQ(2, observer.auth_supplied_count());
1134  EXPECT_EQ(2, observer.auth_needed_count());
1135  EXPECT_EQ(0, observer.auth_cancelled_count());
1136  EXPECT_TRUE(test_server()->Stop());
1137}
1138
1139// If an XMLHttpRequest is made without credentials, there should be a login
1140// prompt.  If it's cancelled, the script should get a 401 status.
1141IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
1142                       LoginPromptForXHRWithoutCredentialsCancelled) {
1143  const char* kXHRTestPage = "files/login/xhr_without_credentials.html";
1144
1145  ASSERT_TRUE(test_server()->Start());
1146
1147  content::WebContents* contents =
1148      browser()->tab_strip_model()->GetActiveWebContents();
1149  NavigationController* controller = &contents->GetController();
1150  LoginPromptBrowserTestObserver observer;
1151
1152  observer.Register(content::Source<NavigationController>(controller));
1153
1154  // Load a page which makes a synchronous XMLHttpRequest for an authenticated
1155  // resource with the wrong credentials.  There should be no login prompt.
1156  {
1157    GURL test_page = test_server()->GetURL(kXHRTestPage);
1158    WindowedAuthNeededObserver auth_needed_waiter(controller);
1159    browser()->OpenURL(OpenURLParams(
1160        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
1161        false));
1162    auth_needed_waiter.Wait();
1163  }
1164
1165  ASSERT_EQ(1u, observer.handlers().size());
1166  WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
1167  LoginHandler* handler = *observer.handlers().begin();
1168
1169  handler->CancelAuth();
1170  auth_cancelled_waiter.Wait();
1171
1172  WindowedLoadStopObserver load_stop_waiter(controller, 1);
1173  load_stop_waiter.Wait();
1174
1175  base::string16 expected_title(base::UTF8ToUTF16("status=401"));
1176
1177  EXPECT_EQ(expected_title, contents->GetTitle());
1178  EXPECT_EQ(0, observer.auth_supplied_count());
1179  EXPECT_EQ(1, observer.auth_needed_count());
1180  EXPECT_EQ(1, observer.auth_cancelled_count());
1181  EXPECT_TRUE(test_server()->Stop());
1182}
1183
1184// If a cross origin navigation triggers a login prompt, the destination URL
1185// should be shown in the omnibox.
1186void LoginPromptBrowserTest::TestCrossOriginPrompt(
1187    const GURL& visit_url,
1188    const std::string& auth_host) const {
1189  content::WebContents* contents =
1190      browser()->tab_strip_model()->GetActiveWebContents();
1191  NavigationController* controller = &contents->GetController();
1192  LoginPromptBrowserTestObserver observer;
1193
1194  observer.Register(content::Source<NavigationController>(controller));
1195
1196  // Load a page which will trigger a login prompt.
1197  {
1198    WindowedAuthNeededObserver auth_needed_waiter(controller);
1199    browser()->OpenURL(OpenURLParams(
1200        visit_url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
1201        false));
1202    ASSERT_EQ(visit_url.host(), contents->GetVisibleURL().host());
1203    auth_needed_waiter.Wait();
1204    ASSERT_EQ(1u, observer.handlers().size());
1205    WaitForInterstitialAttach(contents);
1206
1207    // The omnibox should show the correct origin for the new page when the
1208    // login prompt is shown.
1209    EXPECT_EQ(auth_host, contents->GetVisibleURL().host());
1210    EXPECT_TRUE(contents->ShowingInterstitialPage());
1211
1212    // Cancel and wait for the interstitial to detach.
1213    LoginHandler* handler = *observer.handlers().begin();
1214    scoped_refptr<content::MessageLoopRunner> loop_runner(
1215        new content::MessageLoopRunner);
1216    InterstitialObserver interstitial_observer(contents,
1217                                               base::Closure(),
1218                                               loop_runner->QuitClosure());
1219    handler->CancelAuth();
1220    if (content::InterstitialPage::GetInterstitialPage(contents))
1221      loop_runner->Run();
1222    EXPECT_EQ(auth_host, contents->GetVisibleURL().host());
1223    EXPECT_FALSE(contents->ShowingInterstitialPage());
1224  }
1225}
1226
1227// If a cross origin direct navigation triggers a login prompt, the login
1228// interstitial should be shown.
1229IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
1230                       ShowCorrectUrlForCrossOriginMainFrameRequests) {
1231  ASSERT_TRUE(test_server()->Start());
1232
1233  GURL test_page = test_server()->GetURL(kAuthBasicPage);
1234  ASSERT_EQ("127.0.0.1", test_page.host());
1235  std::string auth_host("127.0.0.1");
1236  TestCrossOriginPrompt(test_page, auth_host);
1237}
1238
1239// If a cross origin redirect triggers a login prompt, the destination URL
1240// should be shown in the omnibox when the auth dialog is displayed.
1241IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
1242                       ShowCorrectUrlForCrossOriginMainFrameRedirects) {
1243  host_resolver()->AddRule("www.a.com", "127.0.0.1");
1244  ASSERT_TRUE(test_server()->Start());
1245
1246  const char* kTestPage = "files/login/cross_origin.html";
1247  GURL test_page = test_server()->GetURL(kTestPage);
1248  ASSERT_EQ("127.0.0.1", test_page.host());
1249  std::string auth_host("www.a.com");
1250  TestCrossOriginPrompt(test_page, auth_host);
1251}
1252
1253IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
1254                       LoginInterstitialShouldReplaceExistingInterstitial) {
1255  net::SpawnedTestServer https_server(
1256      net::SpawnedTestServer::TYPE_HTTPS,
1257      net::SpawnedTestServer::SSLOptions(
1258          net::SpawnedTestServer::SSLOptions::CERT_EXPIRED),
1259      base::FilePath());
1260  ASSERT_TRUE(https_server.Start());
1261
1262  content::WebContents* contents =
1263      browser()->tab_strip_model()->GetActiveWebContents();
1264  NavigationController* controller = &contents->GetController();
1265  LoginPromptBrowserTestObserver observer;
1266
1267  observer.Register(content::Source<NavigationController>(controller));
1268
1269  // Load a page which triggers an SSL interstitial. Proceeding through it
1270  // should show the login page with the blank interstitial.
1271  {
1272    GURL test_page = https_server.GetURL(kAuthBasicPage);
1273    ASSERT_EQ("127.0.0.1", test_page.host());
1274
1275    WindowedAuthNeededObserver auth_needed_waiter(controller);
1276    browser()->OpenURL(OpenURLParams(
1277        test_page, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_TYPED,
1278        false));
1279    ASSERT_EQ("127.0.0.1", contents->GetURL().host());
1280    WaitForInterstitialAttach(contents);
1281
1282    // An overrideable SSL interstitial is now being displayed. Proceed through
1283    // the interstitial to see the login prompt.
1284    contents->GetInterstitialPage()->Proceed();
1285    auth_needed_waiter.Wait();
1286    ASSERT_EQ(1u, observer.handlers().size());
1287    WaitForInterstitialAttach(contents);
1288
1289    // The omnibox should show the correct origin while the login prompt is
1290    // being displayed.
1291    EXPECT_EQ("127.0.0.1", contents->GetVisibleURL().host());
1292    EXPECT_TRUE(contents->ShowingInterstitialPage());
1293
1294    // Cancelling the login prompt should detach the interstitial while keeping
1295    // the correct origin.
1296    LoginHandler* handler = *observer.handlers().begin();
1297    scoped_refptr<content::MessageLoopRunner> loop_runner(
1298        new content::MessageLoopRunner);
1299    InterstitialObserver interstitial_observer(contents,
1300                                               base::Closure(),
1301                                               loop_runner->QuitClosure());
1302    handler->CancelAuth();
1303    if (content::InterstitialPage::GetInterstitialPage(contents))
1304      loop_runner->Run();
1305    EXPECT_EQ("127.0.0.1", contents->GetVisibleURL().host());
1306    EXPECT_FALSE(contents->ShowingInterstitialPage());
1307  }
1308}
1309
1310}  // namespace
1311