| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include <map> | 
 | #include <set> | 
 |  | 
 | #include "base/basictypes.h" | 
 | #include "base/bind.h" | 
 | #include "base/command_line.h" | 
 | #include "base/compiler_specific.h" | 
 | #include "base/file_path.h" | 
 | #include "base/message_loop.h" | 
 | #include "base/path_service.h" | 
 | #include "base/utf_string_conversions.h" | 
 | #include "chrome/browser/captive_portal/captive_portal_service.h" | 
 | #include "chrome/browser/captive_portal/captive_portal_service_factory.h" | 
 | #include "chrome/browser/captive_portal/captive_portal_tab_helper.h" | 
 | #include "chrome/browser/captive_portal/captive_portal_tab_reloader.h" | 
 | #include "chrome/browser/net/url_request_mock_util.h" | 
 | #include "chrome/browser/prefs/pref_service.h" | 
 | #include "chrome/browser/profiles/profile.h" | 
 | #include "chrome/browser/ui/browser.h" | 
 | #include "chrome/browser/ui/browser_commands.h" | 
 | #include "chrome/browser/ui/browser_finder.h" | 
 | #include "chrome/browser/ui/browser_navigator.h" | 
 | #include "chrome/browser/ui/browser_tabstrip.h" | 
 | #include "chrome/browser/ui/tab_contents/tab_contents.h" | 
 | #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" | 
 | #include "chrome/common/chrome_notification_types.h" | 
 | #include "chrome/common/chrome_paths.h" | 
 | #include "chrome/common/chrome_switches.h" | 
 | #include "chrome/common/pref_names.h" | 
 | #include "chrome/test/base/in_process_browser_test.h" | 
 | #include "chrome/test/base/ui_test_utils.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 | #include "content/public/browser/navigation_controller.h" | 
 | #include "content/public/browser/notification_observer.h" | 
 | #include "content/public/browser/notification_registrar.h" | 
 | #include "content/public/browser/notification_service.h" | 
 | #include "content/public/browser/notification_types.h" | 
 | #include "content/public/browser/render_view_host.h" | 
 | #include "content/public/browser/web_contents.h" | 
 | #include "content/public/common/url_constants.h" | 
 | #include "content/test/net/url_request_failed_job.h" | 
 | #include "content/test/net/url_request_mock_http_job.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "net/base/transport_security_state.h" | 
 | #include "net/url_request/url_request.h" | 
 | #include "net/url_request/url_request_context.h" | 
 | #include "net/url_request/url_request_context_getter.h" | 
 | #include "net/url_request/url_request_filter.h" | 
 | #include "net/url_request/url_request_job.h" | 
 | #include "net/url_request/url_request_status.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | using content::BrowserThread; | 
 |  | 
 | namespace captive_portal { | 
 |  | 
 | namespace { | 
 |  | 
 | // Path of the fake login page, when using the TestServer. | 
 | const char* const kTestServerLoginPath = "files/captive_portal/login.html"; | 
 |  | 
 | // Path of a page with an iframe that has a mock SSL timeout, when using the | 
 | // TestServer. | 
 | const char* const kTestServerIframeTimeoutPath = | 
 |     "files/captive_portal/iframe_timeout.html"; | 
 |  | 
 | // The following URLs each have two different behaviors, depending on whether | 
 | // URLRequestMockCaptivePortalJobFactory is currently simulating the presence | 
 | // of a captive portal or not.  They use different domains so that HSTS can be | 
 | // applied to them independently. | 
 |  | 
 | // A mock URL for the CaptivePortalService's |test_url|.  When behind a captive | 
 | // portal, this URL returns a mock login page.  When connected to the Internet, | 
 | // it returns a 204 response.  Uses the name of the login file so that reloading | 
 | // it will not request a different URL. | 
 | const char* const kMockCaptivePortalTestUrl = | 
 |     "http://mock.captive.portal.test/login.html"; | 
 |  | 
 | // Another mock URL for the CaptivePortalService's |test_url|.  When behind a | 
 | // captive portal, this URL returns a 511 status code and an HTML page that | 
 | // redirect to the above URL.  When connected to the Internet, it returns a 204 | 
 | // response. | 
 | const char* const kMockCaptivePortal511Url = | 
 |     "http://mock.captive.portal.511/page511.html"; | 
 |  | 
 | // When behind a captive portal, this URL hangs without committing until a call | 
 | // to URLRequestTimeoutOnDemandJob::FailJobs.  When that function is called, | 
 | // the request will time out. | 
 | // | 
 | // When connected to the Internet, this URL returns a non-error page. | 
 | const char* const kMockHttpsUrl = | 
 |     "https://mock.captive.portal.long.timeout/title2.html"; | 
 |  | 
 | // Same as above, but different domain, so can be used to trigger cross-site | 
 | // navigations. | 
 | const char* const kMockHttpsUrl2 = | 
 |     "https://mock.captive.portal.long.timeout2/title2.html"; | 
 |  | 
 | // Same as kMockHttpsUrl, except the timeout happens instantly. | 
 | const char* const kMockHttpsQuickTimeoutUrl = | 
 |     "https://mock.captive.portal.quick.timeout/title2.html"; | 
 |  | 
 | // Expected title of a tab once an HTTPS load completes, when not behind a | 
 | // captive portal. | 
 | const char* const kInternetConnectedTitle = "Title Of Awesomeness"; | 
 |  | 
 | // A URL request job that hangs until FailJobs() is called.  Started jobs | 
 | // are stored in a static class variable containing a linked list so that | 
 | // FailJobs() can locate them. | 
 | class URLRequestTimeoutOnDemandJob : public net::URLRequestJob, | 
 |                                      public base::NonThreadSafe { | 
 |  public: | 
 |   // net::URLRequestJob: | 
 |   virtual void Start() OVERRIDE; | 
 |  | 
 |   // All the public static methods below can be called on any thread. | 
 |  | 
 |   // Waits for exactly |num_jobs|. | 
 |   static void WaitForJobs(int num_jobs); | 
 |  | 
 |   // Fails all active URLRequestTimeoutOnDemandJobs with connection timeouts. | 
 |   // There are expected to be exactly |expected_num_jobs| waiting for | 
 |   // failure.  The only way to gaurantee this is with an earlier call to | 
 |   // WaitForJobs, so makes sure there has been a matching WaitForJobs call. | 
 |   static void FailJobs(int expected_num_jobs); | 
 |  | 
 |   // Abandon all active URLRequestTimeoutOnDemandJobs.  |expected_num_jobs| | 
 |   // behaves just as in FailJobs. | 
 |   static void AbandonJobs(int expected_num_jobs); | 
 |  | 
 |  private: | 
 |   friend class URLRequestMockCaptivePortalJobFactory; | 
 |  | 
 |   // Operation to perform on jobs when removing them from |job_list_|. | 
 |   enum EndJobOperation { | 
 |     FAIL_JOBS, | 
 |     ABANDON_JOBS, | 
 |   }; | 
 |  | 
 |   URLRequestTimeoutOnDemandJob(net::URLRequest* request, | 
 |                                net::NetworkDelegate* network_delegate); | 
 |   virtual ~URLRequestTimeoutOnDemandJob(); | 
 |  | 
 |   // Attempts to removes |this| from |jobs_|.  Returns true if it was removed | 
 |   // from the list. | 
 |   bool RemoveFromList(); | 
 |  | 
 |   static void WaitForJobsOnIOThread(int num_jobs); | 
 |   static void FailOrAbandonJobsOnIOThread( | 
 |       int expected_num_jobs, | 
 |       EndJobOperation end_job_operation); | 
 |  | 
 |   // Checks if there are at least |num_jobs_to_wait_for_| jobs in | 
 |   // |job_list_|.  If so, exits the message loop on the UI thread, which | 
 |   // should be spinning in a call to WaitForJobs.  Does nothing when | 
 |   // |num_jobs_to_wait_for_| is 0. | 
 |   static void MaybeStopWaitingForJobsOnIOThread(); | 
 |  | 
 |   // All class variables are only accessed on the IO thread. | 
 |  | 
 |   // Number of jobs currently being waited for, or 0 if not currently | 
 |   // waiting for jobs. | 
 |   static int num_jobs_to_wait_for_; | 
 |  | 
 |   // The last number of jobs that were waited for.  When FailJobs or | 
 |   // AbandonJobs is called, this should match |expected_num_jobs|. | 
 |   static int last_num_jobs_to_wait_for_; | 
 |  | 
 |   // Number of jobs that have been started, but not yet waited for.  If jobs | 
 |   // are deleted unexpectedly, they're still included in this count, even though | 
 |   // they've been removed from |job_list_|.  Intended to reduce chance of stalls | 
 |   // on regressions. | 
 |   static int num_jobs_started_; | 
 |  | 
 |   // Head of linked list of jobs that have been started and are now waiting to | 
 |   // be timed out. | 
 |   static URLRequestTimeoutOnDemandJob* job_list_; | 
 |  | 
 |   // The next job that had been started but not yet timed out. | 
 |   URLRequestTimeoutOnDemandJob* next_job_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(URLRequestTimeoutOnDemandJob); | 
 | }; | 
 |  | 
 | int URLRequestTimeoutOnDemandJob::num_jobs_to_wait_for_ = 0; | 
 | int URLRequestTimeoutOnDemandJob::last_num_jobs_to_wait_for_ = 0; | 
 | int URLRequestTimeoutOnDemandJob::num_jobs_started_ = 0; | 
 | URLRequestTimeoutOnDemandJob* URLRequestTimeoutOnDemandJob::job_list_ = NULL; | 
 |  | 
 | void URLRequestTimeoutOnDemandJob::Start() { | 
 |   EXPECT_TRUE(CalledOnValidThread()); | 
 |  | 
 |   // Insert at start of the list. | 
 |   next_job_ = job_list_; | 
 |   job_list_ = this; | 
 |   ++num_jobs_started_; | 
 |  | 
 |   // Checks if there are at least |num_jobs_to_wait_for_| jobs in | 
 |   // |job_list_|.  If so, exits the message loop on the UI thread, which | 
 |   // should be spinning in a call to WaitForJobs.  Does nothing if | 
 |   // |num_jobs_to_wait_for_| is 0. | 
 |   MaybeStopWaitingForJobsOnIOThread(); | 
 | } | 
 |  | 
 | // static | 
 | void URLRequestTimeoutOnDemandJob::WaitForJobs(int num_jobs) { | 
 |   content::BrowserThread::PostTask( | 
 |       content::BrowserThread::IO, FROM_HERE, | 
 |       base::Bind(&URLRequestTimeoutOnDemandJob::WaitForJobsOnIOThread, | 
 |                  num_jobs)); | 
 |   content::RunMessageLoop(); | 
 | } | 
 |  | 
 | // static | 
 | void URLRequestTimeoutOnDemandJob::FailJobs(int expected_num_jobs) { | 
 |   content::BrowserThread::PostTask( | 
 |       content::BrowserThread::IO, FROM_HERE, | 
 |       base::Bind(&URLRequestTimeoutOnDemandJob::FailOrAbandonJobsOnIOThread, | 
 |                  expected_num_jobs, | 
 |                  FAIL_JOBS)); | 
 | } | 
 |  | 
 | // static | 
 | void URLRequestTimeoutOnDemandJob::AbandonJobs(int expected_num_jobs) { | 
 |   content::BrowserThread::PostTask( | 
 |       content::BrowserThread::IO, FROM_HERE, | 
 |       base::Bind(&URLRequestTimeoutOnDemandJob::FailOrAbandonJobsOnIOThread, | 
 |                  expected_num_jobs, | 
 |                  ABANDON_JOBS)); | 
 | } | 
 |  | 
 | URLRequestTimeoutOnDemandJob::URLRequestTimeoutOnDemandJob( | 
 |     net::URLRequest* request, net::NetworkDelegate* network_delegate) | 
 |     : net::URLRequestJob(request, network_delegate), | 
 |       next_job_(NULL) { | 
 | } | 
 |  | 
 | URLRequestTimeoutOnDemandJob::~URLRequestTimeoutOnDemandJob() { | 
 |   // All hanging jobs should have failed or been abandoned before being | 
 |   // destroyed. | 
 |   EXPECT_FALSE(RemoveFromList()); | 
 | } | 
 |  | 
 | bool URLRequestTimeoutOnDemandJob::RemoveFromList() { | 
 |   URLRequestTimeoutOnDemandJob** job = &job_list_; | 
 |   while (*job) { | 
 |     if (*job == this) { | 
 |       *job = next_job_; | 
 |       next_job_ = NULL; | 
 |       return true; | 
 |     } | 
 |     job = &next_job_; | 
 |   } | 
 |  | 
 |   // If the job wasn't in this list, |next_job_| should be NULL. | 
 |   EXPECT_FALSE(next_job_); | 
 |   return false; | 
 | } | 
 |  | 
 | // static | 
 | void URLRequestTimeoutOnDemandJob::WaitForJobsOnIOThread(int num_jobs) { | 
 |   ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 
 |   ASSERT_EQ(0, num_jobs_to_wait_for_); | 
 |   ASSERT_LT(0, num_jobs); | 
 |   // Number of tabs being waited on should be strictly increasing. | 
 |   ASSERT_LE(last_num_jobs_to_wait_for_, num_jobs); | 
 |  | 
 |   num_jobs_to_wait_for_ = num_jobs; | 
 |   MaybeStopWaitingForJobsOnIOThread(); | 
 | } | 
 |  | 
 | // static | 
 | void URLRequestTimeoutOnDemandJob::MaybeStopWaitingForJobsOnIOThread() { | 
 |   ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 
 |   if (num_jobs_to_wait_for_ == 0) | 
 |     return; | 
 |  | 
 |   // There shouldn't be any extra jobs. | 
 |   EXPECT_LE(num_jobs_started_, num_jobs_to_wait_for_); | 
 |  | 
 |   // Should never be greater, but if it is, go ahead and exit the message loop | 
 |   // to try and avoid hanging. | 
 |   if (num_jobs_started_ >= num_jobs_to_wait_for_) { | 
 |     last_num_jobs_to_wait_for_ = num_jobs_to_wait_for_; | 
 |     num_jobs_to_wait_for_ = 0; | 
 |     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 
 |                             MessageLoop::QuitClosure()); | 
 |   } | 
 | } | 
 |  | 
 | // static | 
 | void URLRequestTimeoutOnDemandJob::FailOrAbandonJobsOnIOThread( | 
 |     int expected_num_jobs, | 
 |     EndJobOperation end_job_operation) { | 
 |   ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 
 |   ASSERT_LT(0, expected_num_jobs); | 
 |   EXPECT_EQ(last_num_jobs_to_wait_for_, expected_num_jobs); | 
 |   last_num_jobs_to_wait_for_ = 0; | 
 |  | 
 |   int num_jobs = 0; | 
 |   while (job_list_) { | 
 |     ++num_jobs; | 
 |     URLRequestTimeoutOnDemandJob* job = job_list_; | 
 |     // Since the error notification may result in the job's destruction, remove | 
 |     // it from the job list before the error. | 
 |     EXPECT_TRUE(job->RemoveFromList()); | 
 |     if (end_job_operation == FAIL_JOBS) { | 
 |       job->NotifyStartError(net::URLRequestStatus( | 
 |                                 net::URLRequestStatus::FAILED, | 
 |                                 net::ERR_CONNECTION_TIMED_OUT)); | 
 |     } | 
 |   } | 
 |  | 
 |   EXPECT_EQ(expected_num_jobs, num_jobs_started_); | 
 |   EXPECT_EQ(expected_num_jobs, num_jobs); | 
 |  | 
 |   num_jobs_started_ -= expected_num_jobs; | 
 | } | 
 |  | 
 | // URLRequestCaptivePortalJobFactory emulates captive portal behavior. | 
 | // Initially, it emulates being behind a captive portal.  When | 
 | // SetBehindCaptivePortal(false) is called, it emulates behavior when not behind | 
 | // a captive portal.  The class itself is never instantiated. | 
 | // | 
 | // It handles requests for kMockCaptivePortalTestUrl, kMockHttpsUrl, and | 
 | // kMockHttpsQuickTimeoutUrl. | 
 | class URLRequestMockCaptivePortalJobFactory { | 
 |  public: | 
 |   // The public static methods below can be called on any thread. | 
 |  | 
 |   // Adds the testing URLs to the net::URLRequestFilter.  Should only be called | 
 |   // once. | 
 |   static void AddUrlHandlers(); | 
 |  | 
 |   // Sets whether or not there is a captive portal.  Outstanding requests are | 
 |   // not affected. | 
 |   static void SetBehindCaptivePortal(bool behind_captive_portal); | 
 |  | 
 |  private: | 
 |   // These do all the work of the corresponding public functions, with the only | 
 |   // difference being that they must be called on the IO thread. | 
 |   static void AddUrlHandlersOnIOThread(); | 
 |   static void SetBehindCaptivePortalOnIOThread(bool behind_captive_portal); | 
 |  | 
 |   // Returns a URLRequestJob that reflects the current captive portal state | 
 |   // for the URLs: kMockCaptivePortalTestUrl, kMockHttpsUrl, and | 
 |   // kMockHttpsQuickTimeoutUrl.  See documentation of individual URLs for | 
 |   // actual behavior. | 
 |   static net::URLRequestJob* Factory(net::URLRequest* request, | 
 |                                      net::NetworkDelegate* network_delegate, | 
 |                                      const std::string& scheme); | 
 |  | 
 |   static bool behind_captive_portal_; | 
 |  | 
 |   DISALLOW_IMPLICIT_CONSTRUCTORS(URLRequestMockCaptivePortalJobFactory); | 
 | }; | 
 |  | 
 | bool URLRequestMockCaptivePortalJobFactory::behind_captive_portal_ = true; | 
 |  | 
 | // static | 
 | void URLRequestMockCaptivePortalJobFactory::AddUrlHandlers() { | 
 |   content::BrowserThread::PostTask( | 
 |       content::BrowserThread::IO, FROM_HERE, | 
 |       base::Bind( | 
 |           &URLRequestMockCaptivePortalJobFactory::AddUrlHandlersOnIOThread)); | 
 | } | 
 |  | 
 | // static | 
 | void URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortal( | 
 |     bool behind_captive_portal) { | 
 |   content::BrowserThread::PostTask( | 
 |       content::BrowserThread::IO, FROM_HERE, | 
 |       base::Bind( | 
 |           &URLRequestMockCaptivePortalJobFactory:: | 
 |               SetBehindCaptivePortalOnIOThread, | 
 |           behind_captive_portal)); | 
 | } | 
 |  | 
 | // static | 
 | void URLRequestMockCaptivePortalJobFactory::AddUrlHandlersOnIOThread() { | 
 |   EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 
 |  | 
 |   // Handle only exact matches, so any related requests, such as those for | 
 |   // favicons, are not handled by the factory. | 
 |   net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance(); | 
 |   filter->AddUrlHandler(GURL(kMockCaptivePortalTestUrl), | 
 |                         URLRequestMockCaptivePortalJobFactory::Factory); | 
 |   filter->AddUrlHandler(GURL(kMockCaptivePortal511Url), | 
 |                         URLRequestMockCaptivePortalJobFactory::Factory); | 
 |   filter->AddUrlHandler(GURL(kMockHttpsUrl), | 
 |                         URLRequestMockCaptivePortalJobFactory::Factory); | 
 |   filter->AddUrlHandler(GURL(kMockHttpsUrl2), | 
 |                         URLRequestMockCaptivePortalJobFactory::Factory); | 
 |   filter->AddUrlHandler(GURL(kMockHttpsQuickTimeoutUrl), | 
 |                         URLRequestMockCaptivePortalJobFactory::Factory); | 
 | } | 
 |  | 
 | // static | 
 | void URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortalOnIOThread( | 
 |     bool behind_captive_portal) { | 
 |   EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 
 |   behind_captive_portal_ = behind_captive_portal; | 
 | } | 
 |  | 
 | // static | 
 | net::URLRequestJob* URLRequestMockCaptivePortalJobFactory::Factory( | 
 |     net::URLRequest* request, | 
 |     net::NetworkDelegate* network_delegate, | 
 |     const std::string& scheme) { | 
 |   EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 
 |  | 
 |   // The PathService is threadsafe. | 
 |   FilePath root_http; | 
 |   PathService::Get(chrome::DIR_TEST_DATA, &root_http); | 
 |  | 
 |   if (request->url() == GURL(kMockHttpsUrl) || | 
 |       request->url() == GURL(kMockHttpsUrl2)) { | 
 |     if (behind_captive_portal_) | 
 |       return new URLRequestTimeoutOnDemandJob(request, network_delegate); | 
 |     // Once logged in to the portal, HTTPS requests return the page that was | 
 |     // actually requested. | 
 |     return new URLRequestMockHTTPJob( | 
 |         request, | 
 |         network_delegate, | 
 |         root_http.Append(FILE_PATH_LITERAL("title2.html"))); | 
 |   } else if (request->url() == GURL(kMockHttpsQuickTimeoutUrl)) { | 
 |     if (behind_captive_portal_) | 
 |       return new URLRequestFailedJob( | 
 |           request, network_delegate, net::ERR_CONNECTION_TIMED_OUT); | 
 |     // Once logged in to the portal, HTTPS requests return the page that was | 
 |     // actually requested. | 
 |     return new URLRequestMockHTTPJob( | 
 |         request, | 
 |         network_delegate, | 
 |         root_http.Append(FILE_PATH_LITERAL("title2.html"))); | 
 |   } else { | 
 |     // The URL should be the captive portal test URL. | 
 |     EXPECT_TRUE(GURL(kMockCaptivePortalTestUrl) == request->url() || | 
 |                 GURL(kMockCaptivePortal511Url) == request->url()); | 
 |  | 
 |     if (behind_captive_portal_) { | 
 |       // Prior to logging in to the portal, the HTTP test URLs are intercepted | 
 |       // by the captive portal. | 
 |       if (GURL(kMockCaptivePortal511Url) == request->url()) { | 
 |         return new URLRequestMockHTTPJob( | 
 |             request, | 
 |             network_delegate, | 
 |             root_http.Append(FILE_PATH_LITERAL("captive_portal/page511.html"))); | 
 |       } | 
 |       return new URLRequestMockHTTPJob( | 
 |           request, | 
 |           network_delegate, | 
 |           root_http.Append(FILE_PATH_LITERAL("captive_portal/login.html"))); | 
 |     } | 
 |  | 
 |     // After logging in to the portal, the test URLs return a 204 response. | 
 |     return new URLRequestMockHTTPJob( | 
 |         request, | 
 |         network_delegate, | 
 |         root_http.Append(FILE_PATH_LITERAL("captive_portal/page204.html"))); | 
 |   } | 
 | } | 
 |  | 
 | // Creates a server-side redirect for use with the TestServer. | 
 | std::string CreateServerRedirect(const std::string& dest_url) { | 
 |   const char* const kServerRedirectBase = "server-redirect?"; | 
 |   return kServerRedirectBase + dest_url; | 
 | } | 
 |  | 
 | // Returns the total number of loading tabs across all Browsers, for all | 
 | // Profiles. | 
 | int NumLoadingTabs() { | 
 |   int num_loading_tabs = 0; | 
 |   for (TabContentsIterator tab_contents_it; | 
 |        !tab_contents_it.done(); | 
 |        ++tab_contents_it) { | 
 |     if (tab_contents_it->web_contents()->IsLoading()) | 
 |       ++num_loading_tabs; | 
 |   } | 
 |   return num_loading_tabs; | 
 | } | 
 |  | 
 | bool IsLoginTab(TabContents* tab_contents) { | 
 |   return CaptivePortalTabHelper::FromWebContents( | 
 |       tab_contents->web_contents())->IsLoginTab(); | 
 | } | 
 |  | 
 | // Tracks how many times each tab has been navigated since the Observer was | 
 | // created.  The standard TestNavigationObserver can only watch specific | 
 | // pre-existing tabs or loads in serial for all tabs. | 
 | class MultiNavigationObserver : public content::NotificationObserver { | 
 |  public: | 
 |   MultiNavigationObserver(); | 
 |   virtual ~MultiNavigationObserver(); | 
 |  | 
 |   // Waits for exactly |num_navigations_to_wait_for| LOAD_STOP | 
 |   // notifications to have occurred since the construction of |this|.  More | 
 |   // navigations than expected occuring will trigger a expect failure. | 
 |   void WaitForNavigations(int num_navigations_to_wait_for); | 
 |  | 
 |   // Returns the number of LOAD_STOP events that have occurred for | 
 |   // |web_contents| since this was constructed. | 
 |   int NumNavigationsForTab(content::WebContents* web_contents) const; | 
 |  | 
 |   // The number of LOAD_STOP events since |this| was created. | 
 |   int num_navigations() const { return num_navigations_; } | 
 |  | 
 |  private: | 
 |   typedef std::map<const content::WebContents*, int> TabNavigationMap; | 
 |  | 
 |   // content::NotificationObserver: | 
 |   virtual void Observe(int type, const content::NotificationSource& source, | 
 |                        const content::NotificationDetails& details) OVERRIDE; | 
 |  | 
 |   int num_navigations_; | 
 |  | 
 |   // Map of how many times each tab has navigated since |this| was created. | 
 |   TabNavigationMap tab_navigation_map_; | 
 |  | 
 |   // Total number of navigations to wait for.  Value only matters when | 
 |   // |waiting_for_navigation_| is true. | 
 |   int num_navigations_to_wait_for_; | 
 |  | 
 |   // True if WaitForNavigations has been called, until | 
 |   // |num_navigations_to_wait_for_| have been observed. | 
 |   bool waiting_for_navigation_; | 
 |  | 
 |   content::NotificationRegistrar registrar_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(MultiNavigationObserver); | 
 | }; | 
 |  | 
 | MultiNavigationObserver::MultiNavigationObserver() | 
 |     : num_navigations_(0), | 
 |       num_navigations_to_wait_for_(0), | 
 |       waiting_for_navigation_(false) { | 
 |   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, | 
 |                  content::NotificationService::AllSources()); | 
 | } | 
 |  | 
 | MultiNavigationObserver::~MultiNavigationObserver() { | 
 | } | 
 |  | 
 | void MultiNavigationObserver::WaitForNavigations( | 
 |     int num_navigations_to_wait_for) { | 
 |   // Shouldn't already be waiting for navigations. | 
 |   EXPECT_FALSE(waiting_for_navigation_); | 
 |   EXPECT_LT(0, num_navigations_to_wait_for); | 
 |   if (num_navigations_ < num_navigations_to_wait_for) { | 
 |     num_navigations_to_wait_for_ = num_navigations_to_wait_for; | 
 |     waiting_for_navigation_ = true; | 
 |     content::RunMessageLoop(); | 
 |     EXPECT_FALSE(waiting_for_navigation_); | 
 |   } | 
 |   EXPECT_EQ(num_navigations_, num_navigations_to_wait_for); | 
 | } | 
 |  | 
 | int MultiNavigationObserver::NumNavigationsForTab( | 
 |     content::WebContents* web_contents) const { | 
 |   TabNavigationMap::const_iterator tab_navigations = | 
 |       tab_navigation_map_.find(web_contents); | 
 |   if (tab_navigations == tab_navigation_map_.end()) | 
 |     return 0; | 
 |   return tab_navigations->second; | 
 | } | 
 |  | 
 | void MultiNavigationObserver::Observe( | 
 |     int type, | 
 |     const content::NotificationSource& source, | 
 |     const content::NotificationDetails& details) { | 
 |   ASSERT_EQ(type, content::NOTIFICATION_LOAD_STOP); | 
 |   content::NavigationController* controller = | 
 |       content::Source<content::NavigationController>(source).ptr(); | 
 |   ++num_navigations_; | 
 |   ++tab_navigation_map_[controller->GetWebContents()]; | 
 |   if (waiting_for_navigation_ && | 
 |       num_navigations_to_wait_for_ == num_navigations_) { | 
 |     waiting_for_navigation_ = false; | 
 |     MessageLoopForUI::current()->Quit(); | 
 |   } | 
 | } | 
 |  | 
 | // This observer creates a list of loading tabs, and then waits for them all | 
 | // to stop loading and have the kInternetConnectedTitle. | 
 | // | 
 | // This is for the specific purpose of observing tabs time out after logging in | 
 | // to a captive portal, which will then cause them to reload. | 
 | // MultiNavigationObserver is insufficient for this because there may or may not | 
 | // be a LOAD_STOP event between the timeout and the reload. | 
 | // See bug http://crbug.com/133227 | 
 | class FailLoadsAfterLoginObserver : public content::NotificationObserver { | 
 |  public: | 
 |   FailLoadsAfterLoginObserver(); | 
 |   virtual ~FailLoadsAfterLoginObserver(); | 
 |  | 
 |   void WaitForNavigations(); | 
 |  | 
 |  private: | 
 |   typedef std::set<const content::WebContents*> TabSet; | 
 |  | 
 |   // content::NotificationObserver: | 
 |   virtual void Observe(int type, const content::NotificationSource& source, | 
 |                        const content::NotificationDetails& details) OVERRIDE; | 
 |  | 
 |   // The set of tabs that need to be navigated.  This is the set of loading | 
 |   // tabs when the observer is created. | 
 |   TabSet tabs_needing_navigation_; | 
 |  | 
 |   // Number of tabs that have stopped navigating with the expected title.  These | 
 |   // are expected not to be navigated again. | 
 |   TabSet tabs_navigated_to_final_destination_; | 
 |  | 
 |   // True if WaitForNavigations has been called, until | 
 |   // |tabs_navigated_to_final_destination_| equals |tabs_needing_navigation_|. | 
 |   bool waiting_for_navigation_; | 
 |  | 
 |   content::NotificationRegistrar registrar_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(FailLoadsAfterLoginObserver); | 
 | }; | 
 |  | 
 | FailLoadsAfterLoginObserver::FailLoadsAfterLoginObserver() | 
 |     : waiting_for_navigation_(false) { | 
 |   registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, | 
 |                  content::NotificationService::AllSources()); | 
 |   for (TabContentsIterator tab_contents_it; | 
 |        !tab_contents_it.done(); | 
 |        ++tab_contents_it) { | 
 |     if (tab_contents_it->web_contents()->IsLoading()) | 
 |       tabs_needing_navigation_.insert(tab_contents_it->web_contents()); | 
 |   } | 
 | } | 
 |  | 
 | FailLoadsAfterLoginObserver::~FailLoadsAfterLoginObserver() { | 
 | } | 
 |  | 
 | void FailLoadsAfterLoginObserver::WaitForNavigations() { | 
 |   // Shouldn't already be waiting for navigations. | 
 |   EXPECT_FALSE(waiting_for_navigation_); | 
 |   if (tabs_needing_navigation_.size() != | 
 |           tabs_navigated_to_final_destination_.size()) { | 
 |     waiting_for_navigation_ = true; | 
 |     content::RunMessageLoop(); | 
 |     EXPECT_FALSE(waiting_for_navigation_); | 
 |   } | 
 |   EXPECT_EQ(tabs_needing_navigation_.size(), | 
 |             tabs_navigated_to_final_destination_.size()); | 
 | } | 
 |  | 
 | void FailLoadsAfterLoginObserver::Observe( | 
 |     int type, | 
 |     const content::NotificationSource& source, | 
 |     const content::NotificationDetails& details) { | 
 |   ASSERT_EQ(type, content::NOTIFICATION_LOAD_STOP); | 
 |   content::NavigationController* controller = | 
 |       content::Source<content::NavigationController>(source).ptr(); | 
 |   content::WebContents* contents = controller->GetWebContents(); | 
 |  | 
 |   ASSERT_EQ(1u, tabs_needing_navigation_.count(contents)); | 
 |   ASSERT_EQ(0u, tabs_navigated_to_final_destination_.count(contents)); | 
 |  | 
 |   if (contents->GetTitle() != ASCIIToUTF16(kInternetConnectedTitle)) | 
 |     return; | 
 |   tabs_navigated_to_final_destination_.insert(contents); | 
 |  | 
 |   if (waiting_for_navigation_ && | 
 |       tabs_needing_navigation_.size() == | 
 |           tabs_navigated_to_final_destination_.size()) { | 
 |     waiting_for_navigation_ = false; | 
 |     MessageLoopForUI::current()->Quit(); | 
 |   } | 
 | } | 
 |  | 
 | // An observer for watching the CaptivePortalService.  It tracks the last | 
 | // received result and the total number of received results. | 
 | class CaptivePortalObserver : public content::NotificationObserver { | 
 |  public: | 
 |   explicit CaptivePortalObserver(Profile* profile); | 
 |  | 
 |   // Runs the message loop until until at exactly |update_count| capitive portal | 
 |   // results have been received, since this creation of |this|.  Expects no | 
 |   // additional captive portal results. | 
 |   void WaitForResults(int num_results_to_wait_for); | 
 |  | 
 |   int num_results_received() const { return num_results_received_; } | 
 |  | 
 |   Result captive_portal_result() const { | 
 |     return captive_portal_result_; | 
 |   } | 
 |  | 
 |  private: | 
 |   // Records results and exits the message loop, if needed. | 
 |   void Observe(int type, | 
 |                const content::NotificationSource& source, | 
 |                const content::NotificationDetails& details); | 
 |  | 
 |   // Number of times OnPortalResult has been called since construction. | 
 |   int num_results_received_; | 
 |  | 
 |   // If WaitForResults was called, the total number of updates for which to | 
 |   // wait.  Value doesn't matter when |waiting_for_result_| is false. | 
 |   int num_results_to_wait_for_; | 
 |  | 
 |   bool waiting_for_result_; | 
 |  | 
 |   Profile* profile_; | 
 |  | 
 |   CaptivePortalService* captive_portal_service_; | 
 |  | 
 |   // Last result received. | 
 |   Result captive_portal_result_; | 
 |  | 
 |   content::NotificationRegistrar registrar_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(CaptivePortalObserver); | 
 | }; | 
 |  | 
 | CaptivePortalObserver::CaptivePortalObserver(Profile* profile) | 
 |     : num_results_received_(0), | 
 |       num_results_to_wait_for_(0), | 
 |       waiting_for_result_(false), | 
 |       profile_(profile), | 
 |       captive_portal_service_( | 
 |           CaptivePortalServiceFactory::GetForProfile(profile)), | 
 |       captive_portal_result_( | 
 |           captive_portal_service_->last_detection_result()) { | 
 |   registrar_.Add(this, | 
 |                  chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT, | 
 |                  content::Source<Profile>(profile_)); | 
 | } | 
 |  | 
 | void CaptivePortalObserver::WaitForResults(int num_results_to_wait_for) { | 
 |   EXPECT_LT(0, num_results_to_wait_for); | 
 |   EXPECT_FALSE(waiting_for_result_); | 
 |   if (num_results_received_ < num_results_to_wait_for) { | 
 |     num_results_to_wait_for_ = num_results_to_wait_for; | 
 |     waiting_for_result_ = true; | 
 |     content::RunMessageLoop(); | 
 |     EXPECT_FALSE(waiting_for_result_); | 
 |   } | 
 |   EXPECT_EQ(num_results_to_wait_for, num_results_received_); | 
 | } | 
 |  | 
 | void CaptivePortalObserver::Observe( | 
 |     int type, | 
 |     const content::NotificationSource& source, | 
 |     const content::NotificationDetails& details) { | 
 |   ASSERT_EQ(type, chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT); | 
 |   ASSERT_EQ(profile_, content::Source<Profile>(source).ptr()); | 
 |  | 
 |   CaptivePortalService::Results* results = | 
 |       content::Details<CaptivePortalService::Results>(details).ptr(); | 
 |  | 
 |   EXPECT_EQ(captive_portal_result_, results->previous_result); | 
 |   EXPECT_EQ(captive_portal_service_->last_detection_result(), | 
 |             results->result); | 
 |  | 
 |   captive_portal_result_ = results->result; | 
 |   ++num_results_received_; | 
 |  | 
 |   if (waiting_for_result_ && | 
 |       num_results_to_wait_for_ == num_results_received_) { | 
 |     waiting_for_result_ = false; | 
 |     MessageLoop::current()->Quit(); | 
 |   } | 
 | } | 
 |  | 
 | // Adds an HSTS rule for |host|, so that all HTTP requests sent to it will | 
 | // be switched to HTTPS requests. | 
 | void AddHstsHost(net::URLRequestContextGetter* context_getter, | 
 |                  const std::string& host) { | 
 |   ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 
 |   net::TransportSecurityState* transport_security_state = | 
 |       context_getter->GetURLRequestContext()->transport_security_state(); | 
 |   if (!transport_security_state) { | 
 |     FAIL(); | 
 |     return; | 
 |   } | 
 |  | 
 |   net::TransportSecurityState::DomainState state; | 
 |   state.upgrade_expiry = state.created + base::TimeDelta::FromDays(1000); | 
 |   state.include_subdomains = false; | 
 |  | 
 |   transport_security_state->EnableHost(host, state); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | class CaptivePortalBrowserTest : public InProcessBrowserTest { | 
 |  public: | 
 |   CaptivePortalBrowserTest(); | 
 |  | 
 |   // InProcessBrowserTest: | 
 |   virtual void SetUpOnMainThread() OVERRIDE; | 
 |   virtual void CleanUpOnMainThread() OVERRIDE; | 
 |  | 
 |   // Sets the captive portal checking preference.  Does not affect the command | 
 |   // line flag, which is set in SetUpCommandLine. | 
 |   void EnableCaptivePortalDetection(Profile* profile, bool enabled); | 
 |  | 
 |   // Sets up the captive portal service for the given profile so that | 
 |   // all checks go to |test_url|.  Also disables all timers. | 
 |   void SetUpCaptivePortalService(Profile* profile, const GURL& test_url); | 
 |  | 
 |   // Returns true if |browser|'s profile is currently running a captive portal | 
 |   // check. | 
 |   bool CheckPending(Browser* browser); | 
 |  | 
 |   // Returns the CaptivePortalTabReloader::State of |tab_contents|. | 
 |   CaptivePortalTabReloader::State GetStateOfTabReloader( | 
 |       TabContents* tab_contents) const; | 
 |  | 
 |   // Returns the CaptivePortalTabReloader::State of the indicated tab. | 
 |   CaptivePortalTabReloader::State GetStateOfTabReloaderAt(Browser* browser, | 
 |                                                           int index) const; | 
 |  | 
 |   // Returns the number of tabs with the given state, across all profiles. | 
 |   int NumTabsWithState(CaptivePortalTabReloader::State state) const; | 
 |  | 
 |   // Returns the number of tabs broken by captive portals, across all profiles. | 
 |   int NumBrokenTabs() const; | 
 |  | 
 |   // Returns the number of tabs that need to be reloaded due to having logged | 
 |   // in to a captive portal, across all profiles. | 
 |   int NumNeedReloadTabs() const; | 
 |  | 
 |   // Navigates |browser|'s active tab to |url| and expects no captive portal | 
 |   // test to be triggered.  |expected_navigations| is the number of times the | 
 |   // active tab will end up being navigated.  It should be 1, except for the | 
 |   // Link Doctor page, which acts like two navigations. | 
 |   void NavigateToPageExpectNoTest(Browser* browser, | 
 |                                   const GURL& url, | 
 |                                   int expected_navigations); | 
 |  | 
 |   // Navigates |browser|'s active tab to an SSL tab that takes a while to load, | 
 |   // triggering a captive portal check, which is expected to give the result | 
 |   // |expected_result|.  The page finishes loading, with a timeout, after the | 
 |   // captive portal check. | 
 |   void SlowLoadNoCaptivePortal(Browser* browser, Result expected_result); | 
 |  | 
 |   // Navigates |browser|'s active tab to an SSL timeout, expecting a captive | 
 |   // portal check to be triggered and return a result which will indicates | 
 |   // there's no detected captive portal. | 
 |   void FastTimeoutNoCaptivePortal(Browser* browser, Result expected_result); | 
 |  | 
 |   // Navigates the active tab to a slow loading SSL page, which will then | 
 |   // trigger a captive portal test.  The test is expected to find a captive | 
 |   // portal.  The slow loading page will continue to load after the function | 
 |   // returns, until URLRequestTimeoutOnDemandJob::FailJobs() is called, | 
 |   // at which point it will timeout. | 
 |   // | 
 |   // When |expect_login_tab| is false, no login tab is expected to be opened, | 
 |   // because one already exists, and the function returns once the captive | 
 |   // portal test is complete. | 
 |   // | 
 |   // If |expect_login_tab| is true, a login tab is then expected to be opened. | 
 |   // It waits until both the login tab has finished loading, and two captive | 
 |   // portal tests complete.  The second test is triggered by the load of the | 
 |   // captive portal tab completing. | 
 |   // | 
 |   // This function must not be called when the active tab is currently loading. | 
 |   // Waits for the hanging request to be issued, so other functions can rely | 
 |   // on URLRequestTimeoutOnDemandJob::WaitForJobs having been called. | 
 |   void SlowLoadBehindCaptivePortal(Browser* browser, bool expect_login_tab); | 
 |  | 
 |   // Same as above, but takes extra parameters. | 
 |   // | 
 |   // |hanging_url| should either be kMockHttpsUrl or redirect to kMockHttpsUrl. | 
 |   // | 
 |   // |expected_portal_checks| and |expected_login_tab_navigations| allow | 
 |   // client-side redirects to be tested.  |expected_login_tab_navigations| is | 
 |   // ignored when |expect_open_login_tab| is false. | 
 |   void SlowLoadBehindCaptivePortal(Browser* browser, | 
 |                                    bool expect_open_login_tab, | 
 |                                    const GURL& hanging_url, | 
 |                                    int expected_portal_checks, | 
 |                                    int expected_login_tab_navigations); | 
 |  | 
 |   // Just like SlowLoadBehindCaptivePortal, except the navigated tab has | 
 |   // a connection timeout rather having its time trigger, and the function | 
 |   // waits until that timeout occurs. | 
 |   void FastTimeoutBehindCaptivePortal(Browser* browser, | 
 |                                       bool expect_open_login_tab); | 
 |  | 
 |   // Much as above, but accepts a URL parameter and can be used for errors that | 
 |   // trigger captive portal checks other than timeouts.  |error_url| should | 
 |   // result in an error rather than hanging. | 
 |   void FastErrorBehindCaptivePortal(Browser* browser, | 
 |                                     bool expect_open_login_tab, | 
 |                                     const GURL& error_url); | 
 |  | 
 |   // Navigates the login tab without logging in.  The login tab must be the | 
 |   // specified browser's active tab.  Expects no other tab to change state. | 
 |   // |num_loading_tabs| and |num_timed_out_tabs| are used as extra checks | 
 |   // that nothing has gone wrong prior to the function call. | 
 |   void NavigateLoginTab(Browser* browser, | 
 |                         int num_loading_tabs, | 
 |                         int num_timed_out_tabs); | 
 |  | 
 |   // Simulates a login by updating the URLRequestMockCaptivePortalJob's | 
 |   // behind captive portal state, and navigating the login tab.  Waits for | 
 |   // all broken but not loading tabs to be reloaded. | 
 |   // |num_loading_tabs| and |num_timed_out_tabs| are used as extra checks | 
 |   // that nothing has gone wrong prior to the function call. | 
 |   void Login(Browser* browser, int num_loading_tabs, int num_timed_out_tabs); | 
 |  | 
 |   // Makes the slow SSL loads of all active tabs time out at once, and waits for | 
 |   // them to finish both that load and the automatic reload it should trigger. | 
 |   // There should be no timed out tabs when this is called. | 
 |   void FailLoadsAfterLogin(Browser* browser, int num_loading_tabs); | 
 |  | 
 |   // Makes the slow SSL loads of all active tabs time out at once, and waits for | 
 |   // them to finish displaying their error pages.  The login tab should be the | 
 |   // active tab.  There should be no timed out tabs when this is called. | 
 |   void FailLoadsWithoutLogin(Browser* browser, int num_loading_tabs); | 
 |  | 
 |   // Navigates |browser|'s active tab to |starting_url| while not behind a | 
 |   // captive portal.  Then navigates to |interrupted_url|, which should create | 
 |   // a URLRequestTimeoutOnDemandJob, which is then abandoned.  The load should | 
 |   // trigger a captive portal check, which finds a captive portal and opens a | 
 |   // tab. | 
 |   // | 
 |   // Then the navigation is interrupted by a navigation to |timeout_url|, which | 
 |   // should trigger a captive portal check, and finally the test simulates | 
 |   // logging in. | 
 |   // | 
 |   // The purpose of this test is to make sure the TabHelper triggers a captive | 
 |   // portal check when a load is interrupted by another load, particularly in | 
 |   // the case of cross-process navigations. | 
 |   void RunNavigateLoadingTabToTimeoutTest(Browser* browser, | 
 |                                           const GURL& starting_url, | 
 |                                           const GURL& interrupted_url, | 
 |                                           const GURL& timeout_url); | 
 |  | 
 |   // Sets the timeout used by a CaptivePortalTabReloader on slow SSL loads | 
 |   // before a captive portal check. | 
 |   void SetSlowSSLLoadTime(CaptivePortalTabReloader* tab_reloader, | 
 |                           base::TimeDelta slow_ssl_load_time); | 
 |  | 
 |   CaptivePortalTabReloader* GetTabReloader(TabContents* tab_contents) const; | 
 |  | 
 |  private: | 
 |   DISALLOW_COPY_AND_ASSIGN(CaptivePortalBrowserTest); | 
 | }; | 
 |  | 
 | CaptivePortalBrowserTest::CaptivePortalBrowserTest() { | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::SetUpOnMainThread() { | 
 |   // Enable mock requests. | 
 |   content::BrowserThread::PostTask( | 
 |       content::BrowserThread::IO, FROM_HERE, | 
 |       base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled, true)); | 
 |   URLRequestMockCaptivePortalJobFactory::AddUrlHandlers(); | 
 |  | 
 |   // Double-check that the captive portal service isn't enabled by default for | 
 |   // browser tests. | 
 |   EXPECT_EQ(CaptivePortalService::DISABLED_FOR_TESTING, | 
 |             CaptivePortalService::get_state_for_testing()); | 
 |  | 
 |   CaptivePortalService::set_state_for_testing( | 
 |       CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); | 
 |   EnableCaptivePortalDetection(browser()->profile(), true); | 
 |  | 
 |   // Set the captive portal service to use URLRequestMockCaptivePortalJob's | 
 |   // mock URL, by default. | 
 |   SetUpCaptivePortalService(browser()->profile(), | 
 |                             GURL(kMockCaptivePortalTestUrl)); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::CleanUpOnMainThread() { | 
 |   // No test should have a captive portal check pending on quit. | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::EnableCaptivePortalDetection( | 
 |     Profile* profile, bool enabled) { | 
 |   profile->GetPrefs()->SetBoolean(prefs::kAlternateErrorPagesEnabled, enabled); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::SetUpCaptivePortalService(Profile* profile, | 
 |                                                          const GURL& test_url) { | 
 |   CaptivePortalService* captive_portal_service = | 
 |       CaptivePortalServiceFactory::GetForProfile(profile); | 
 |   captive_portal_service->set_test_url(test_url); | 
 |  | 
 |   // Don't use any non-zero timers.  Timers are checked in unit tests. | 
 |   CaptivePortalService::RecheckPolicy* recheck_policy = | 
 |       &captive_portal_service->recheck_policy(); | 
 |   recheck_policy->initial_backoff_no_portal_ms = 0; | 
 |   recheck_policy->initial_backoff_portal_ms = 0; | 
 |   recheck_policy->backoff_policy.maximum_backoff_ms = 0; | 
 | } | 
 |  | 
 | bool CaptivePortalBrowserTest::CheckPending(Browser* browser) { | 
 |   CaptivePortalService* captive_portal_service = | 
 |       CaptivePortalServiceFactory::GetForProfile(browser->profile()); | 
 |  | 
 |   return captive_portal_service->FetchingURL() || | 
 |       captive_portal_service->TimerRunning(); | 
 | } | 
 |  | 
 | CaptivePortalTabReloader::State CaptivePortalBrowserTest::GetStateOfTabReloader( | 
 |     TabContents* tab_contents) const { | 
 |   return GetTabReloader(tab_contents)->state(); | 
 | } | 
 |  | 
 | CaptivePortalTabReloader::State | 
 | CaptivePortalBrowserTest::GetStateOfTabReloaderAt(Browser* browser, | 
 |                                                   int index) const { | 
 |   return GetStateOfTabReloader(chrome::GetTabContentsAt(browser, index)); | 
 | } | 
 |  | 
 | int CaptivePortalBrowserTest::NumTabsWithState( | 
 |     CaptivePortalTabReloader::State state) const { | 
 |   int num_tabs = 0; | 
 |   for (TabContentsIterator tab_contents_it; | 
 |        !tab_contents_it.done(); | 
 |        ++tab_contents_it) { | 
 |     if (GetStateOfTabReloader(*tab_contents_it) == state) | 
 |       ++num_tabs; | 
 |   } | 
 |   return num_tabs; | 
 | } | 
 |  | 
 | int CaptivePortalBrowserTest::NumBrokenTabs() const { | 
 |   return NumTabsWithState(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL); | 
 | } | 
 |  | 
 | int CaptivePortalBrowserTest::NumNeedReloadTabs() const { | 
 |   return NumTabsWithState(CaptivePortalTabReloader::STATE_NEEDS_RELOAD); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::NavigateToPageExpectNoTest( | 
 |     Browser* browser, | 
 |     const GURL& url, | 
 |     int expected_navigations) { | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |  | 
 |   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( | 
 |       browser, url, expected_navigations); | 
 |  | 
 |   // No captive portal checks should have ocurred or be pending, and there | 
 |   // should be no new tabs. | 
 |   EXPECT_EQ(0, portal_observer.num_results_received()); | 
 |   EXPECT_FALSE(CheckPending(browser)); | 
 |   EXPECT_EQ(1, browser->tab_count()); | 
 |   EXPECT_EQ(expected_navigations, navigation_observer.num_navigations()); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser, 0)); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::SlowLoadNoCaptivePortal( | 
 |     Browser* browser, Result expected_result) { | 
 |   CaptivePortalTabReloader* tab_reloader = | 
 |       GetTabReloader(chrome::GetActiveTabContents(browser)); | 
 |   ASSERT_TRUE(tab_reloader); | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta()); | 
 |  | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |   ui_test_utils::NavigateToURLWithDisposition(browser, | 
 |                                               GURL(kMockHttpsUrl), | 
 |                                               CURRENT_TAB, | 
 |                                               ui_test_utils::BROWSER_TEST_NONE); | 
 |  | 
 |   portal_observer.WaitForResults(1); | 
 |  | 
 |   ASSERT_EQ(1, browser->tab_count()); | 
 |   EXPECT_EQ(expected_result, portal_observer.captive_portal_result()); | 
 |   EXPECT_EQ(1, portal_observer.num_results_received()); | 
 |   EXPECT_EQ(0, navigation_observer.num_navigations()); | 
 |   EXPECT_FALSE(CheckPending(browser)); | 
 |  | 
 |   // First tab should still be loading. | 
 |   EXPECT_EQ(1, NumLoadingTabs()); | 
 |  | 
 |   // Wait for the request to be issued, then time it out. | 
 |   URLRequestTimeoutOnDemandJob::WaitForJobs(1); | 
 |   URLRequestTimeoutOnDemandJob::FailJobs(1); | 
 |   navigation_observer.WaitForNavigations(1); | 
 |  | 
 |   ASSERT_EQ(1, browser->tab_count()); | 
 |   EXPECT_EQ(1, portal_observer.num_results_received()); | 
 |   EXPECT_FALSE(CheckPending(browser)); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |  | 
 |   // Set a slow SSL load time to prevent the timer from triggering. | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromDays(1)); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::FastTimeoutNoCaptivePortal( | 
 |     Browser* browser, Result expected_result) { | 
 |   ASSERT_NE(expected_result, RESULT_BEHIND_CAPTIVE_PORTAL); | 
 |  | 
 |   // Set the load time to be large, so the timer won't trigger.  The value is | 
 |   // not restored at the end of the function. | 
 |   CaptivePortalTabReloader* tab_reloader = | 
 |       GetTabReloader(chrome::GetActiveTabContents(browser)); | 
 |   ASSERT_TRUE(tab_reloader); | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromHours(1)); | 
 |  | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |  | 
 |   // Neither of these should be changed by the navigation. | 
 |   int active_index = browser->active_index(); | 
 |   int expected_tab_count = browser->tab_count(); | 
 |  | 
 |   ui_test_utils::NavigateToURL( | 
 |       browser, | 
 |       URLRequestFailedJob::GetMockHttpsUrl(net::ERR_CONNECTION_TIMED_OUT)); | 
 |  | 
 |   // An attempt to detect a captive portal should have started by now.  If not, | 
 |   // abort early to prevent hanging. | 
 |   ASSERT_TRUE(portal_observer.num_results_received() > 0 || | 
 |               CheckPending(browser)); | 
 |  | 
 |   portal_observer.WaitForResults(1); | 
 |   navigation_observer.WaitForNavigations(1); | 
 |  | 
 |   // Check the result. | 
 |   EXPECT_EQ(1, portal_observer.num_results_received()); | 
 |   EXPECT_EQ(expected_result, portal_observer.captive_portal_result()); | 
 |  | 
 |   // Check that the right tab was navigated, and there were no extra | 
 |   // navigations. | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    chrome::GetWebContentsAt(browser, active_index))); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |  | 
 |   // Check the tab's state, and verify no captive portal check is pending. | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser, 0)); | 
 |   EXPECT_FALSE(CheckPending(browser)); | 
 |  | 
 |   // Make sure no login tab was opened. | 
 |   EXPECT_EQ(expected_tab_count, browser->tab_count()); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::SlowLoadBehindCaptivePortal( | 
 |     Browser* browser, | 
 |     bool expect_open_login_tab) { | 
 |   SlowLoadBehindCaptivePortal(browser, | 
 |                               expect_open_login_tab, | 
 |                               GURL(kMockHttpsUrl), | 
 |                               1, | 
 |                               1); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::SlowLoadBehindCaptivePortal( | 
 |     Browser* browser, | 
 |     bool expect_open_login_tab, | 
 |     const GURL& hanging_url, | 
 |     int expected_portal_checks, | 
 |     int expected_login_tab_navigations) { | 
 |   ASSERT_GE(expected_portal_checks, 1); | 
 |   // Calling this on a tab that's waiting for a load to manually be timed out | 
 |   // will result in a hang. | 
 |   ASSERT_FALSE(chrome::GetActiveWebContents(browser)->IsLoading()); | 
 |  | 
 |   // Trigger a captive portal check quickly. | 
 |   CaptivePortalTabReloader* tab_reloader = | 
 |       GetTabReloader(chrome::GetActiveTabContents(browser)); | 
 |   ASSERT_TRUE(tab_reloader); | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta()); | 
 |  | 
 |   // Number of tabs expected to be open after the captive portal checks | 
 |   // have completed. | 
 |   int initial_tab_count = browser->tab_count(); | 
 |   int initial_active_index = browser->active_index(); | 
 |   int initial_loading_tabs = NumLoadingTabs(); | 
 |   int expected_broken_tabs = NumBrokenTabs(); | 
 |   if (CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL != | 
 |           GetStateOfTabReloader(chrome::GetActiveTabContents(browser))) { | 
 |     ++expected_broken_tabs; | 
 |   } | 
 |  | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |   ui_test_utils::NavigateToURLWithDisposition(browser, | 
 |                                               hanging_url, | 
 |                                               CURRENT_TAB, | 
 |                                               ui_test_utils::BROWSER_TEST_NONE); | 
 |   portal_observer.WaitForResults(expected_portal_checks); | 
 |  | 
 |   if (expect_open_login_tab) { | 
 |     ASSERT_GE(expected_login_tab_navigations, 1); | 
 |  | 
 |     navigation_observer.WaitForNavigations(expected_login_tab_navigations); | 
 |  | 
 |     ASSERT_EQ(initial_tab_count + 1, browser->tab_count()); | 
 |     EXPECT_EQ(initial_tab_count, browser->active_index()); | 
 |  | 
 |     EXPECT_EQ(expected_login_tab_navigations, | 
 |               navigation_observer.NumNavigationsForTab( | 
 |                   chrome::GetWebContentsAt(browser, initial_tab_count))); | 
 |     EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |               GetStateOfTabReloader(chrome::GetTabContentsAt(browser, 1))); | 
 |     EXPECT_TRUE(IsLoginTab(chrome::GetTabContentsAt(browser, 1))); | 
 |   } else { | 
 |     EXPECT_EQ(0, navigation_observer.num_navigations()); | 
 |     EXPECT_EQ(initial_active_index, browser->active_index()); | 
 |     ASSERT_EQ(initial_tab_count, browser->tab_count()); | 
 |     EXPECT_EQ(initial_active_index, browser->active_index()); | 
 |   } | 
 |  | 
 |   // Wait for all the expect resource loads to actually start, so subsequent | 
 |   // functions can rely on them having started. | 
 |   URLRequestTimeoutOnDemandJob::WaitForJobs(initial_loading_tabs + 1); | 
 |  | 
 |   EXPECT_EQ(initial_loading_tabs + 1, NumLoadingTabs()); | 
 |   EXPECT_EQ(expected_broken_tabs, NumBrokenTabs()); | 
 |   EXPECT_EQ(RESULT_BEHIND_CAPTIVE_PORTAL, | 
 |             portal_observer.captive_portal_result()); | 
 |   EXPECT_EQ(expected_portal_checks, portal_observer.num_results_received()); | 
 |   EXPECT_FALSE(CheckPending(browser)); | 
 |  | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL, | 
 |             GetStateOfTabReloader( | 
 |                 chrome::GetTabContentsAt(browser, initial_active_index))); | 
 |  | 
 |   // Reset the load time to be large, so the timer won't trigger on a reload. | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromHours(1)); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::FastTimeoutBehindCaptivePortal( | 
 |     Browser* browser, | 
 |     bool expect_open_login_tab) { | 
 |   FastErrorBehindCaptivePortal(browser, | 
 |                                expect_open_login_tab, | 
 |                                GURL(kMockHttpsQuickTimeoutUrl)); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::FastErrorBehindCaptivePortal( | 
 |     Browser* browser, | 
 |     bool expect_open_login_tab, | 
 |     const GURL& error_url) { | 
 |   // Calling this on a tab that's waiting for a load to manually be timed out | 
 |   // will result in a hang. | 
 |   ASSERT_FALSE(chrome::GetActiveWebContents(browser)->IsLoading()); | 
 |  | 
 |   // Set the load time to be large, so the timer won't trigger.  The value is | 
 |   // not restored at the end of the function. | 
 |   CaptivePortalTabReloader* tab_reloader = | 
 |       GetTabReloader(chrome::GetActiveTabContents(browser)); | 
 |   ASSERT_TRUE(tab_reloader); | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromHours(1)); | 
 |  | 
 |   // Number of tabs expected to be open after the captive portal checks | 
 |   // have completed. | 
 |   int initial_tab_count = browser->tab_count(); | 
 |   int initial_active_index = browser->active_index(); | 
 |   int initial_loading_tabs = NumLoadingTabs(); | 
 |   int expected_broken_tabs = NumBrokenTabs(); | 
 |   if (CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL != | 
 |           GetStateOfTabReloader(chrome::GetActiveTabContents(browser))) { | 
 |     ++expected_broken_tabs; | 
 |   } | 
 |  | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |   ui_test_utils::NavigateToURLWithDisposition(browser, | 
 |                                               error_url, | 
 |                                               CURRENT_TAB, | 
 |                                               ui_test_utils::BROWSER_TEST_NONE); | 
 |   portal_observer.WaitForResults(1); | 
 |  | 
 |   if (expect_open_login_tab) { | 
 |     navigation_observer.WaitForNavigations(2); | 
 |     ASSERT_EQ(initial_tab_count + 1, browser->tab_count()); | 
 |     EXPECT_EQ(initial_tab_count, browser->active_index()); | 
 |     // Make sure that the originally active tab and the captive portal tab have | 
 |     // each loaded once. | 
 |     EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                      chrome::GetWebContentsAt(browser, initial_active_index))); | 
 |     EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                      chrome::GetWebContentsAt(browser, initial_tab_count))); | 
 |     EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |               GetStateOfTabReloader(chrome::GetTabContentsAt(browser, 1))); | 
 |     EXPECT_TRUE(IsLoginTab(chrome::GetTabContentsAt(browser, 1))); | 
 |   } else { | 
 |     navigation_observer.WaitForNavigations(1); | 
 |     EXPECT_EQ(initial_active_index, browser->active_index()); | 
 |     EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                      chrome::GetWebContentsAt(browser, initial_active_index))); | 
 |     ASSERT_EQ(initial_tab_count, browser->tab_count()); | 
 |     EXPECT_EQ(initial_active_index, browser->active_index()); | 
 |   } | 
 |  | 
 |   EXPECT_EQ(initial_loading_tabs, NumLoadingTabs()); | 
 |   EXPECT_EQ(expected_broken_tabs, NumBrokenTabs()); | 
 |   EXPECT_EQ(RESULT_BEHIND_CAPTIVE_PORTAL, | 
 |             portal_observer.captive_portal_result()); | 
 |   EXPECT_EQ(1, portal_observer.num_results_received()); | 
 |   EXPECT_FALSE(CheckPending(browser)); | 
 |  | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL, | 
 |             GetStateOfTabReloader( | 
 |                 chrome::GetTabContentsAt(browser, initial_active_index))); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::NavigateLoginTab(Browser* browser, | 
 |                                                 int num_loading_tabs, | 
 |                                                 int num_timed_out_tabs) { | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |  | 
 |   int initial_tab_count = browser->tab_count(); | 
 |   EXPECT_EQ(num_loading_tabs, NumLoadingTabs()); | 
 |   EXPECT_EQ(num_timed_out_tabs, NumBrokenTabs() - NumLoadingTabs()); | 
 |  | 
 |   int login_tab_index = browser->active_index(); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloader(chrome::GetActiveTabContents(browser))); | 
 |   ASSERT_TRUE(IsLoginTab(chrome::GetActiveTabContents(browser))); | 
 |  | 
 |   // Do the navigation. | 
 |   content::RenderViewHost* render_view_host = | 
 |       chrome::GetActiveWebContents(browser)->GetRenderViewHost(); | 
 |   render_view_host->ExecuteJavascriptInWebFrame( | 
 |       string16(), | 
 |       ASCIIToUTF16("submitForm()")); | 
 |  | 
 |   portal_observer.WaitForResults(1); | 
 |   navigation_observer.WaitForNavigations(1); | 
 |  | 
 |   // Check the captive portal result. | 
 |   EXPECT_EQ(RESULT_BEHIND_CAPTIVE_PORTAL, | 
 |             portal_observer.captive_portal_result()); | 
 |   EXPECT_EQ(1, portal_observer.num_results_received()); | 
 |   EXPECT_FALSE(CheckPending(browser)); | 
 |  | 
 |   // Make sure not much has changed. | 
 |   EXPECT_EQ(initial_tab_count, browser->tab_count()); | 
 |   EXPECT_EQ(num_loading_tabs, NumLoadingTabs()); | 
 |   EXPECT_EQ(num_loading_tabs + num_timed_out_tabs, NumBrokenTabs()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloader(chrome::GetTabContentsAt(browser, | 
 |                                                            login_tab_index))); | 
 |   EXPECT_TRUE(IsLoginTab(chrome::GetTabContentsAt(browser, login_tab_index))); | 
 |  | 
 |   // Make sure there were no unexpected navigations. | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    chrome::GetWebContentsAt(browser, login_tab_index))); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::Login(Browser* browser, | 
 |                                      int num_loading_tabs, | 
 |                                      int num_timed_out_tabs) { | 
 |   // Simulate logging in. | 
 |   URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortal(false); | 
 |  | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |  | 
 |   int initial_tab_count = browser->tab_count(); | 
 |   ASSERT_EQ(num_loading_tabs, NumLoadingTabs()); | 
 |   EXPECT_EQ(num_timed_out_tabs, NumBrokenTabs() - NumLoadingTabs()); | 
 |  | 
 |   // Verify that the login page is on top. | 
 |   int login_tab_index = browser->active_index(); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloader(chrome::GetTabContentsAt(browser, | 
 |                                                            login_tab_index))); | 
 |   ASSERT_TRUE(IsLoginTab(chrome::GetTabContentsAt(browser, login_tab_index))); | 
 |  | 
 |   // Trigger a navigation. | 
 |   content::RenderViewHost* render_view_host = | 
 |       chrome::GetActiveWebContents(browser)->GetRenderViewHost(); | 
 |   render_view_host->ExecuteJavascriptInWebFrame( | 
 |       string16(), | 
 |       ASCIIToUTF16("submitForm()")); | 
 |  | 
 |   portal_observer.WaitForResults(1); | 
 |  | 
 |   // Wait for all the timed out tabs to reload. | 
 |   navigation_observer.WaitForNavigations(1 + num_timed_out_tabs); | 
 |   EXPECT_EQ(1, portal_observer.num_results_received()); | 
 |  | 
 |   // The tabs that were loading before should still be loading, and now be in | 
 |   // STATE_NEEDS_RELOAD. | 
 |   EXPECT_EQ(0, NumBrokenTabs()); | 
 |   EXPECT_EQ(num_loading_tabs, NumLoadingTabs()); | 
 |   EXPECT_EQ(num_loading_tabs, NumNeedReloadTabs()); | 
 |  | 
 |   // Make sure that the broken tabs have reloaded, and there's no more | 
 |   // captive portal tab. | 
 |   EXPECT_EQ(initial_tab_count, browser->tab_count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser, login_tab_index)); | 
 |   EXPECT_FALSE(IsLoginTab(chrome::GetTabContentsAt(browser, login_tab_index))); | 
 |  | 
 |   // Make sure there were no unexpected navigations of the login tab. | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    chrome::GetWebContentsAt(browser, login_tab_index))); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::FailLoadsAfterLogin(Browser* browser, | 
 |                                                    int num_loading_tabs) { | 
 |   ASSERT_EQ(num_loading_tabs, NumLoadingTabs()); | 
 |   ASSERT_EQ(num_loading_tabs, NumNeedReloadTabs()); | 
 |   EXPECT_EQ(0, NumBrokenTabs()); | 
 |  | 
 |   int initial_num_tabs = browser->tab_count(); | 
 |   int initial_active_tab = browser->active_index(); | 
 |  | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |   FailLoadsAfterLoginObserver fail_loads_observer; | 
 |   // Connection(s) finally time out.  There should have already been a call | 
 |   // to wait for the requests to be issued before logging on. | 
 |   URLRequestTimeoutOnDemandJob::WaitForJobs(num_loading_tabs); | 
 |   URLRequestTimeoutOnDemandJob::FailJobs(num_loading_tabs); | 
 |  | 
 |   fail_loads_observer.WaitForNavigations(); | 
 |  | 
 |   // No captive portal checks should have ocurred or be pending, and there | 
 |   // should be no new tabs. | 
 |   EXPECT_EQ(0, portal_observer.num_results_received()); | 
 |   EXPECT_FALSE(CheckPending(browser)); | 
 |   EXPECT_EQ(initial_num_tabs, browser->tab_count()); | 
 |  | 
 |   EXPECT_EQ(initial_active_tab, browser->active_index()); | 
 |  | 
 |   EXPECT_EQ(0, NumNeedReloadTabs()); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::FailLoadsWithoutLogin(Browser* browser, | 
 |                                                      int num_loading_tabs) { | 
 |   ASSERT_EQ(num_loading_tabs, NumLoadingTabs()); | 
 |   ASSERT_EQ(0, NumNeedReloadTabs()); | 
 |   EXPECT_EQ(num_loading_tabs, NumBrokenTabs()); | 
 |  | 
 |   int initial_num_tabs = browser->tab_count(); | 
 |   int login_tab = browser->active_index(); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloader(chrome::GetActiveTabContents(browser))); | 
 |   ASSERT_TRUE(IsLoginTab(chrome::GetActiveTabContents(browser))); | 
 |  | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |   MultiNavigationObserver navigation_observer; | 
 |   // Connection(s) finally time out.  There should have already been a call | 
 |   // to wait for the requests to be issued. | 
 |   URLRequestTimeoutOnDemandJob::FailJobs(num_loading_tabs); | 
 |  | 
 |   navigation_observer.WaitForNavigations(num_loading_tabs); | 
 |  | 
 |   // No captive portal checks should have ocurred or be pending, and there | 
 |   // should be no new tabs. | 
 |   EXPECT_EQ(0, portal_observer.num_results_received()); | 
 |   EXPECT_FALSE(CheckPending(browser)); | 
 |   EXPECT_EQ(initial_num_tabs, browser->tab_count()); | 
 |  | 
 |   EXPECT_EQ(0, NumNeedReloadTabs()); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |   EXPECT_EQ(num_loading_tabs, NumBrokenTabs()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloader(chrome::GetActiveTabContents(browser))); | 
 |   EXPECT_TRUE(IsLoginTab(chrome::GetActiveTabContents(browser))); | 
 |   EXPECT_EQ(login_tab, browser->active_index()); | 
 |  | 
 |   EXPECT_EQ(0, navigation_observer.NumNavigationsForTab( | 
 |                    chrome::GetWebContentsAt(browser, login_tab))); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::RunNavigateLoadingTabToTimeoutTest( | 
 |     Browser* browser, | 
 |     const GURL& starting_url, | 
 |     const GURL& hanging_url, | 
 |     const GURL& timeout_url) { | 
 |   // Temporarily disable the captive portal and navigate to the starting | 
 |   // URL, which may be a URL that will hang when behind a captive portal. | 
 |   URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortal(false); | 
 |   NavigateToPageExpectNoTest(browser, starting_url, 1); | 
 |   URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortal(true); | 
 |  | 
 |   // Go to the first hanging url. | 
 |   SlowLoadBehindCaptivePortal(browser, true, hanging_url, 1, 1); | 
 |  | 
 |   // Abandon the request. | 
 |   URLRequestTimeoutOnDemandJob::WaitForJobs(1); | 
 |   URLRequestTimeoutOnDemandJob::AbandonJobs(1); | 
 |  | 
 |   CaptivePortalTabReloader* tab_reloader = | 
 |       GetTabReloader(chrome::GetTabContentsAt(browser, 0)); | 
 |   ASSERT_TRUE(tab_reloader); | 
 |  | 
 |   // A non-zero delay makes it more likely that CaptivePortalTabHelper will | 
 |   // be confused by events relating to canceling the old navigation. | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromSeconds(2)); | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |  | 
 |   // Navigate the error tab to another slow loading page.  Can't have | 
 |   // ui_test_utils do the navigation because it will wait for loading tabs to | 
 |   // stop loading before navigating. | 
 |   // | 
 |   // This may result in either 0 or 1 DidStopLoading events.  If there is one, | 
 |   // it must happen before the CaptivePortalService sends out its test request, | 
 |   // so waiting for PortalObserver to see that request prevents it from | 
 |   // confusing the MultiNavigationObservers used later. | 
 |   chrome::ActivateTabAt(browser, 0, true); | 
 |   browser->OpenURL(content::OpenURLParams(timeout_url, | 
 |                                           content::Referrer(), | 
 |                                           CURRENT_TAB, | 
 |                                           content::PAGE_TRANSITION_TYPED, | 
 |                                           false)); | 
 |   portal_observer.WaitForResults(1); | 
 |   EXPECT_FALSE(CheckPending(browser)); | 
 |   EXPECT_EQ(1, NumLoadingTabs()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL, | 
 |             GetStateOfTabReloaderAt(browser, 0)); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloader(chrome::GetTabContentsAt(browser, 1))); | 
 |   ASSERT_TRUE(IsLoginTab(chrome::GetTabContentsAt(browser, 1))); | 
 |  | 
 |   // Need to make sure the request has been issued before logging in. | 
 |   URLRequestTimeoutOnDemandJob::WaitForJobs(1); | 
 |  | 
 |   // Simulate logging in. | 
 |   chrome::ActivateTabAt(browser, 1, true); | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromDays(1)); | 
 |   Login(browser, 1, 0); | 
 |  | 
 |   // Timeout occurs, and page is automatically reloaded. | 
 |   FailLoadsAfterLogin(browser, 1); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::SetSlowSSLLoadTime( | 
 |     CaptivePortalTabReloader* tab_reloader, | 
 |     base::TimeDelta slow_ssl_load_time) { | 
 |   tab_reloader->set_slow_ssl_load_time(slow_ssl_load_time); | 
 | } | 
 |  | 
 | CaptivePortalTabReloader* CaptivePortalBrowserTest::GetTabReloader( | 
 |     TabContents* tab_contents) const { | 
 |   return CaptivePortalTabHelper::FromWebContents( | 
 |       tab_contents->web_contents())->GetTabReloaderForTest(); | 
 | } | 
 |  | 
 | // Make sure there's no test for a captive portal on HTTP timeouts.  This will | 
 | // also trigger the link doctor page, which results in the load of a second | 
 | // error page. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, HttpTimeout) { | 
 |   GURL url = URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_TIMED_OUT); | 
 |   NavigateToPageExpectNoTest(browser(), url, 2); | 
 | } | 
 |  | 
 | // Make sure there's no check for a captive portal on HTTPS errors other than | 
 | // timeouts, when they preempt the slow load timer. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, HttpsNonTimeoutError) { | 
 |   GURL url = URLRequestFailedJob::GetMockHttpsUrl(net::ERR_UNEXPECTED); | 
 |   NavigateToPageExpectNoTest(browser(), url, 1); | 
 | } | 
 |  | 
 | // Make sure no captive portal test triggers on HTTPS timeouts of iframes. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, HttpsIframeTimeout) { | 
 |   // Use an HTTPS server for the top level page. | 
 |   net::TestServer https_server(net::TestServer::TYPE_HTTPS, | 
 |                                net::TestServer::kLocalhost, | 
 |                                FilePath(FILE_PATH_LITERAL("chrome/test/data"))); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |  | 
 |   GURL url = https_server.GetURL(kTestServerIframeTimeoutPath); | 
 |   NavigateToPageExpectNoTest(browser(), url, 1); | 
 | } | 
 |  | 
 | // Check the captive portal result when the test request reports a network | 
 | // error.  The check is triggered by a slow loading page, and the page | 
 | // errors out only after getting a captive portal result. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, RequestFails) { | 
 |   SetUpCaptivePortalService( | 
 |       browser()->profile(), | 
 |       URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_CLOSED)); | 
 |   SlowLoadNoCaptivePortal(browser(), RESULT_NO_RESPONSE); | 
 | } | 
 |  | 
 | // Same as above, but for the rather unlikely case that the connection times out | 
 | // before the timer triggers. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, RequestFailsFastTimout) { | 
 |   SetUpCaptivePortalService( | 
 |       browser()->profile(), | 
 |       URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_CLOSED)); | 
 |   FastTimeoutNoCaptivePortal(browser(), RESULT_NO_RESPONSE); | 
 | } | 
 |  | 
 | // Checks the case that captive portal detection is disabled. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, Disabled) { | 
 |   EnableCaptivePortalDetection(browser()->profile(), false); | 
 |   SlowLoadNoCaptivePortal(browser(), RESULT_INTERNET_CONNECTED); | 
 | } | 
 |  | 
 | // Checks that we look for a captive portal on HTTPS timeouts and don't reload | 
 | // the error tab when the captive portal probe gets a 204 response, indicating | 
 | // there is no captive portal. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, InternetConnected) { | 
 |   // Can't just use SetBehindCaptivePortal(false), since then there wouldn't | 
 |   // be a timeout. | 
 |   ASSERT_TRUE(test_server()->Start()); | 
 |   SetUpCaptivePortalService(browser()->profile(), | 
 |                             test_server()->GetURL("nocontent")); | 
 |   SlowLoadNoCaptivePortal(browser(), RESULT_INTERNET_CONNECTED); | 
 | } | 
 |  | 
 | // Checks that no login page is opened when the HTTP test URL redirects to an | 
 | // SSL certificate error. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, RedirectSSLCertError) { | 
 |   // Need an HTTP TestServer to handle a dynamically created server redirect. | 
 |   ASSERT_TRUE(test_server()->Start()); | 
 |  | 
 |   net::TestServer::SSLOptions ssl_options; | 
 |   ssl_options.server_certificate = | 
 |       net::TestServer::SSLOptions::CERT_MISMATCHED_NAME; | 
 |   net::TestServer https_server(net::TestServer::TYPE_HTTPS, | 
 |                                ssl_options, | 
 |                                FilePath(FILE_PATH_LITERAL("chrome/test/data"))); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |  | 
 |   GURL ssl_login_url = https_server.GetURL(kTestServerLoginPath); | 
 |  | 
 |   CaptivePortalService* captive_portal_service = | 
 |       CaptivePortalServiceFactory::GetForProfile(browser()->profile()); | 
 |   ASSERT_TRUE(captive_portal_service); | 
 |   SetUpCaptivePortalService( | 
 |       browser()->profile(), | 
 |       test_server()->GetURL(CreateServerRedirect(ssl_login_url.spec()))); | 
 |  | 
 |   SlowLoadNoCaptivePortal(browser(), RESULT_NO_RESPONSE); | 
 | } | 
 |  | 
 | // A slow SSL load triggers a captive portal check.  The user logs on before | 
 | // the SSL page times out.  We wait for the timeout and subsequent reload. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, Login) { | 
 |   // Load starts, detect captive portal and open up a login tab. | 
 |   SlowLoadBehindCaptivePortal(browser(), true); | 
 |  | 
 |   // Log in.  One loading tab, no timed out ones. | 
 |   Login(browser(), 1, 0); | 
 |  | 
 |   // Timeout occurs, and page is automatically reloaded. | 
 |   FailLoadsAfterLogin(browser(), 1); | 
 | } | 
 |  | 
 | // Same as above, except we make sure everything works with an incognito | 
 | // profile.  Main issues it tests for are that the incognito has its own | 
 | // non-NULL captive portal service, and we open the tab in the correct | 
 | // window. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginIncognito) { | 
 |   // This will watch tabs for both profiles, but only used to make sure no | 
 |   // navigations occur for the non-incognito profile. | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver non_incognito_portal_observer(browser()->profile()); | 
 |  | 
 |   Browser* incognito_browser = CreateIncognitoBrowser(); | 
 |   EnableCaptivePortalDetection(incognito_browser->profile(), true); | 
 |   SetUpCaptivePortalService(incognito_browser->profile(), | 
 |                             GURL(kMockCaptivePortalTestUrl)); | 
 |  | 
 |   SlowLoadBehindCaptivePortal(incognito_browser, true); | 
 |  | 
 |   EXPECT_EQ(1, browser()->tab_count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |  | 
 |   Login(incognito_browser, 1, 0); | 
 |   FailLoadsAfterLogin(incognito_browser, 1); | 
 |  | 
 |   EXPECT_EQ(1, browser()->tab_count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |  | 
 |   EXPECT_EQ(0, navigation_observer.NumNavigationsForTab( | 
 |                    chrome::GetWebContentsAt(browser(), 0))); | 
 |   EXPECT_EQ(0, non_incognito_portal_observer.num_results_received()); | 
 | } | 
 |  | 
 | // The captive portal page is opened before the SSL page times out, | 
 | // but the user logs in only after the page times out. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginSlow) { | 
 |   SlowLoadBehindCaptivePortal(browser(), true); | 
 |   FailLoadsWithoutLogin(browser(), 1); | 
 |   Login(browser(), 0, 1); | 
 | } | 
 |  | 
 | // Checks the unlikely case that the tab times out before the timer triggers. | 
 | // This most likely won't happen, but should still work: | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginFastTimeout) { | 
 |   FastTimeoutBehindCaptivePortal(browser(), true); | 
 |   Login(browser(), 0, 1); | 
 | } | 
 |  | 
 | // A cert error triggers a captive portal check and results in opening a login | 
 | // tab.  The user then logs in and the page with the error is reloaded. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, SSLCertErrorLogin) { | 
 |   // Need an HTTP TestServer to handle a dynamically created server redirect. | 
 |   ASSERT_TRUE(test_server()->Start()); | 
 |  | 
 |   net::TestServer::SSLOptions https_options; | 
 |   https_options.server_certificate = | 
 |       net::TestServer::SSLOptions::CERT_MISMATCHED_NAME; | 
 |   net::TestServer https_server(net::TestServer::TYPE_HTTPS, | 
 |                                https_options, | 
 |                                FilePath(FILE_PATH_LITERAL("chrome/test/data"))); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |  | 
 |   // The path does not matter. | 
 |   GURL cert_error_url = https_server.GetURL(kTestServerLoginPath); | 
 |   // The interstitial should trigger a captive portal check when it opens, just | 
 |   // like navigating to kMockHttpsQuickTimeoutUrl. | 
 |   FastErrorBehindCaptivePortal(browser(), true, cert_error_url); | 
 |  | 
 |   // Simulate logging in.  Can't use Login() because the interstitial tab looks | 
 |   // like a cross between a hung tab (Load was never committed) and a tab at an | 
 |   // error page (The load was stopped). | 
 |   URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortal(false); | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |  | 
 |   content::RenderViewHost* render_view_host = | 
 |       chrome::GetActiveWebContents(browser())->GetRenderViewHost(); | 
 |   render_view_host->ExecuteJavascriptInWebFrame( | 
 |       string16(), | 
 |       ASCIIToUTF16("submitForm()")); | 
 |  | 
 |   // The captive portal tab navigation will trigger a captive portal check, | 
 |   // and reloading the original tab will bring up the interstitial page again, | 
 |   // triggering a second captive portal check. | 
 |   portal_observer.WaitForResults(2); | 
 |  | 
 |   // Wait for both tabs to finish loading. | 
 |   navigation_observer.WaitForNavigations(2); | 
 |   EXPECT_EQ(2, portal_observer.num_results_received()); | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 |   EXPECT_EQ(captive_portal::RESULT_INTERNET_CONNECTED, | 
 |             portal_observer.captive_portal_result()); | 
 |  | 
 |   // Check state of tabs.  While the first tab is still displaying an | 
 |   // interstitial page, since no portal was found, it should be in STATE_NONE, | 
 |   // as should the login tab. | 
 |   ASSERT_EQ(2, browser()->tab_count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |   EXPECT_FALSE(IsLoginTab(chrome::GetTabContentsAt(browser(), 1))); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 1)); | 
 |  | 
 |   // Make sure only one navigation was for the login tab. | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    chrome::GetWebContentsAt(browser(), 1))); | 
 | } | 
 |  | 
 | // Tries navigating both the tab that encounters an SSL timeout and the | 
 | // login tab twice, only logging in the second time. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginExtraNavigations) { | 
 |   FastTimeoutBehindCaptivePortal(browser(), true); | 
 |  | 
 |   // Activate the timed out tab and navigate it to a timeout again. | 
 |   chrome::ActivateTabAt(browser(), 0, true); | 
 |   FastTimeoutBehindCaptivePortal(browser(), false); | 
 |  | 
 |   // Activate and navigate the captive portal tab.  This should not trigger a | 
 |   // reload of the tab with the error. | 
 |   chrome::ActivateTabAt(browser(), 1, true); | 
 |   NavigateLoginTab(browser(), 0, 1); | 
 |  | 
 |   // Simulate logging in. | 
 |   Login(browser(), 0, 1); | 
 | } | 
 |  | 
 | // After the first SSL timeout, closes the login tab and makes sure it's opened | 
 | // it again on a second timeout. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, CloseLoginTab) { | 
 |   // First load starts, opens a login tab, and then times out. | 
 |   SlowLoadBehindCaptivePortal(browser(), true); | 
 |   FailLoadsWithoutLogin(browser(), 1); | 
 |  | 
 |   // Close login tab. | 
 |   chrome::CloseTab(browser()); | 
 |  | 
 |   // Go through the standard slow load login, and make sure it still works. | 
 |   SlowLoadBehindCaptivePortal(browser(), true); | 
 |   Login(browser(), 1, 0); | 
 |   FailLoadsAfterLogin(browser(), 1); | 
 | } | 
 |  | 
 | // Checks that two tabs with SSL timeouts in the same window work.  Both | 
 | // tabs only timeout after logging in. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, TwoBrokenTabs) { | 
 |   SlowLoadBehindCaptivePortal(browser(), true); | 
 |  | 
 |   // Can't set the TabReloader HTTPS timeout on a new tab without doing some | 
 |   // acrobatics, so open a new tab at a normal page, and then navigate it to a | 
 |   // timeout. | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   ui_test_utils::NavigateToURLWithDisposition( | 
 |       browser(), | 
 |       URLRequestMockHTTPJob::GetMockUrl( | 
 |           FilePath(FILE_PATH_LITERAL("title2.html"))), | 
 |       NEW_FOREGROUND_TAB, | 
 |       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); | 
 |  | 
 |   ASSERT_EQ(3, browser()->tab_count()); | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 |   EXPECT_EQ(0, portal_observer.num_results_received()); | 
 |   EXPECT_EQ(1, NumLoadingTabs()); | 
 |   EXPECT_EQ(1, navigation_observer.num_navigations()); | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    chrome::GetWebContentsAt(browser(), 2))); | 
 |   ASSERT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |     GetStateOfTabReloader(chrome::GetTabContentsAt(browser(), 1))); | 
 |   ASSERT_TRUE(IsLoginTab(chrome::GetTabContentsAt(browser(), 1))); | 
 |   ASSERT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 2)); | 
 |   ASSERT_EQ(2, browser()->active_index()); | 
 |  | 
 |   SlowLoadBehindCaptivePortal(browser(), false); | 
 |  | 
 |   chrome::ActivateTabAt(browser(), 1, true); | 
 |   Login(browser(), 2, 0); | 
 |   FailLoadsAfterLogin(browser(), 2); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, AbortLoad) { | 
 |   SlowLoadBehindCaptivePortal(browser(), true); | 
 |  | 
 |   // Abandon the request. | 
 |   URLRequestTimeoutOnDemandJob::WaitForJobs(1); | 
 |   URLRequestTimeoutOnDemandJob::AbandonJobs(1); | 
 |  | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   MultiNavigationObserver navigation_observer; | 
 |  | 
 |   // Switch back to the hung tab from the login tab, and abort the navigation. | 
 |   chrome::ActivateTabAt(browser(), 0, true); | 
 |   chrome::Stop(browser()); | 
 |   navigation_observer.WaitForNavigations(1); | 
 |  | 
 |   EXPECT_EQ(0, NumBrokenTabs()); | 
 |   EXPECT_EQ(0, portal_observer.num_results_received()); | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |  | 
 |   chrome::ActivateTabAt(browser(), 1, true); | 
 |   Login(browser(), 0, 0); | 
 | } | 
 |  | 
 | // Checks the case where the timed out tab is successfully navigated before | 
 | // logging in. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, NavigateBrokenTab) { | 
 |   // Go to the error page. | 
 |   SlowLoadBehindCaptivePortal(browser(), true); | 
 |   FailLoadsWithoutLogin(browser(), 1); | 
 |  | 
 |   // Navigate the error tab to a non-error page. | 
 |   chrome::ActivateTabAt(browser(), 0, true); | 
 |   ui_test_utils::NavigateToURL(browser(), | 
 |                                URLRequestMockHTTPJob::GetMockUrl( | 
 |                                    FilePath(FILE_PATH_LITERAL("title2.html")))); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |  | 
 |   // Simulate logging in. | 
 |   chrome::ActivateTabAt(browser(), 1, true); | 
 |   Login(browser(), 0, 0); | 
 | } | 
 |  | 
 | // Checks that captive portal detection triggers correctly when a same-site | 
 | // navigation is cancelled by a navigation to the same site. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, | 
 |                        NavigateLoadingTabToTimeoutSingleSite) { | 
 |   RunNavigateLoadingTabToTimeoutTest( | 
 |       browser(), | 
 |       GURL(kMockHttpsUrl), | 
 |       GURL(kMockHttpsUrl), | 
 |       GURL(kMockHttpsUrl)); | 
 | } | 
 |  | 
 | // Checks that captive portal detection triggers correctly when a same-site | 
 | // navigation is cancelled by a navigation to another site. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, | 
 |                        NavigateLoadingTabToTimeoutTwoSites) { | 
 |   RunNavigateLoadingTabToTimeoutTest( | 
 |       browser(), | 
 |       GURL(kMockHttpsUrl), | 
 |       GURL(kMockHttpsUrl), | 
 |       GURL(kMockHttpsUrl2)); | 
 | } | 
 |  | 
 | // Checks that captive portal detection triggers correctly when a cross-site | 
 | // navigation is cancelled by a navigation to yet another site. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, | 
 |                        NavigateLoadingTabToTimeoutThreeSites) { | 
 |   RunNavigateLoadingTabToTimeoutTest( | 
 |       browser(), | 
 |       URLRequestMockHTTPJob::GetMockUrl( | 
 |           FilePath(FILE_PATH_LITERAL("title.html"))), | 
 |       GURL(kMockHttpsUrl), | 
 |       GURL(kMockHttpsUrl2)); | 
 | } | 
 |  | 
 | // Checks that navigating a timed out tab back clears its state. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, GoBack) { | 
 |   // Navigate to a working page. | 
 |   ui_test_utils::NavigateToURL( | 
 |       browser(), | 
 |       URLRequestMockHTTPJob::GetMockUrl( | 
 |           FilePath(FILE_PATH_LITERAL("title2.html")))); | 
 |  | 
 |   // Go to the error page. | 
 |   SlowLoadBehindCaptivePortal(browser(), true); | 
 |   FailLoadsWithoutLogin(browser(), 1); | 
 |  | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   MultiNavigationObserver navigation_observer; | 
 |  | 
 |   // Activate the error page tab again and go back. | 
 |   chrome::ActivateTabAt(browser(), 0, true); | 
 |   chrome::GoBack(browser(), CURRENT_TAB); | 
 |   navigation_observer.WaitForNavigations(1); | 
 |  | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                 chrome::GetWebContentsAt(browser(), 0))); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |   EXPECT_EQ(0, portal_observer.num_results_received()); | 
 | } | 
 |  | 
 | // Checks that navigating back to a timeout triggers captive portal detection. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, GoBackToTimeout) { | 
 |   // Disable captive portal detection so the first navigation doesn't open a | 
 |   // login tab. | 
 |   EnableCaptivePortalDetection(browser()->profile(), false); | 
 |  | 
 |   SlowLoadNoCaptivePortal(browser(), RESULT_INTERNET_CONNECTED); | 
 |  | 
 |   // Navigate to a working page. | 
 |   ui_test_utils::NavigateToURL(browser(), | 
 |                                URLRequestMockHTTPJob::GetMockUrl( | 
 |                                    FilePath(FILE_PATH_LITERAL("title2.html")))); | 
 |   ASSERT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |  | 
 |   EnableCaptivePortalDetection(browser()->profile(), true); | 
 |  | 
 |   CaptivePortalTabReloader* tab_reloader = | 
 |       GetTabReloader(chrome::GetActiveTabContents(browser())); | 
 |   ASSERT_TRUE(tab_reloader); | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta()); | 
 |  | 
 |   // Go to the error page. | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   chrome::GoBack(browser(), CURRENT_TAB); | 
 |  | 
 |   // Wait for the check triggered by the broken tab and for the login tab to | 
 |   // stop loading. | 
 |   portal_observer.WaitForResults(1); | 
 |   navigation_observer.WaitForNavigations(1); | 
 |   // Make sure the request has been issued. | 
 |   URLRequestTimeoutOnDemandJob::WaitForJobs(1); | 
 |  | 
 |   EXPECT_EQ(1, portal_observer.num_results_received()); | 
 |   ASSERT_FALSE(CheckPending(browser())); | 
 |   ASSERT_EQ(RESULT_BEHIND_CAPTIVE_PORTAL, | 
 |             portal_observer.captive_portal_result()); | 
 |  | 
 |   ASSERT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL, | 
 |             GetStateOfTabReloader(chrome::GetTabContentsAt(browser(), 0))); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloader(chrome::GetTabContentsAt(browser(), 1))); | 
 |   ASSERT_TRUE(IsLoginTab(chrome::GetTabContentsAt(browser(), 1))); | 
 |  | 
 |   ASSERT_EQ(2, browser()->tab_count()); | 
 |   EXPECT_EQ(1, browser()->active_index()); | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    chrome::GetWebContentsAt(browser(), 1))); | 
 |   EXPECT_EQ(1, NumLoadingTabs()); | 
 |  | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromDays(1)); | 
 |   Login(browser(), 1, 0); | 
 |   FailLoadsAfterLogin(browser(), 1); | 
 | } | 
 |  | 
 | // Checks that reloading a timeout triggers captive portal detection. | 
 | // Much like the last test, though the captive portal is disabled before | 
 | // the inital navigation, rather than captive portal detection. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, ReloadTimeout) { | 
 |   URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortal(false); | 
 |  | 
 |   // Do the first navigation while not behind a captive portal. | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   ui_test_utils::NavigateToURL(browser(), GURL(kMockHttpsUrl)); | 
 |   ASSERT_EQ(0, portal_observer.num_results_received()); | 
 |   ASSERT_EQ(1, browser()->tab_count()); | 
 |  | 
 |   // A captive portal spontaneously appears. | 
 |   URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortal(true); | 
 |  | 
 |   CaptivePortalTabReloader* tab_reloader = | 
 |       GetTabReloader(chrome::GetActiveTabContents(browser())); | 
 |   ASSERT_TRUE(tab_reloader); | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta()); | 
 |  | 
 |   MultiNavigationObserver navigation_observer; | 
 |   chrome::GetActiveWebContents(browser())->GetController().Reload(true); | 
 |  | 
 |   // Wait for the check triggered by the broken tab and for the login tab to | 
 |   // stop loading. | 
 |   portal_observer.WaitForResults(1); | 
 |   navigation_observer.WaitForNavigations(1); | 
 |   // Make sure the request has been issued. | 
 |   URLRequestTimeoutOnDemandJob::WaitForJobs(1); | 
 |  | 
 |   ASSERT_EQ(1, portal_observer.num_results_received()); | 
 |   ASSERT_FALSE(CheckPending(browser())); | 
 |   ASSERT_EQ(RESULT_BEHIND_CAPTIVE_PORTAL, | 
 |             portal_observer.captive_portal_result()); | 
 |  | 
 |   ASSERT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL, | 
 |       GetStateOfTabReloader(chrome::GetTabContentsAt(browser(), 0))); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloader(chrome::GetTabContentsAt(browser(), 1))); | 
 |   ASSERT_TRUE(IsLoginTab(chrome::GetTabContentsAt(browser(), 1))); | 
 |  | 
 |   ASSERT_EQ(2, browser()->tab_count()); | 
 |   EXPECT_EQ(1, browser()->active_index()); | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    chrome::GetWebContentsAt(browser(), 1))); | 
 |   EXPECT_EQ(1, NumLoadingTabs()); | 
 |  | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromDays(1)); | 
 |   Login(browser(), 1, 0); | 
 |   FailLoadsAfterLogin(browser(), 1); | 
 | } | 
 |  | 
 | // Checks the case where there are two windows, and there's an SSL timeout in | 
 | // the background one. | 
 | // Disabled:  http://crbug.com/134357 | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, DISABLED_TwoWindows) { | 
 |   Browser* browser2 = new Browser(Browser::CreateParams(browser()->profile())); | 
 |   // Navigate the new browser window so it'll be shown and we can pick the | 
 |   // active window. | 
 |   ui_test_utils::NavigateToURL(browser2, GURL(chrome::kAboutBlankURL)); | 
 |  | 
 |   // Generally, |browser2| will be the active window.  However, if the | 
 |   // original browser window lost focus before creating the new one, such as | 
 |   // when running multiple tests at once, the original browser window may | 
 |   // remain the profile's active window. | 
 |   Browser* active_browser = | 
 |       browser::FindTabbedBrowser(browser()->profile(), true); | 
 |   Browser* inactive_browser; | 
 |   if (active_browser == browser2) { | 
 |     // When only one test is running at a time, the new browser will probably be | 
 |     // on top, but when multiple tests are running at once, this is not | 
 |     // guaranteed. | 
 |     inactive_browser = browser(); | 
 |   } else { | 
 |     ASSERT_EQ(active_browser, browser()); | 
 |     inactive_browser = browser2; | 
 |   } | 
 |  | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   MultiNavigationObserver navigation_observer; | 
 |  | 
 |   // Navigate the tab in the inactive browser to an SSL timeout.  Have to use | 
 |   // chrome::NavigateParams and NEW_BACKGROUND_TAB to avoid activating the | 
 |   // window. | 
 |   chrome::NavigateParams params(inactive_browser, | 
 |                                 GURL(kMockHttpsQuickTimeoutUrl), | 
 |                                 content::PAGE_TRANSITION_TYPED); | 
 |   params.disposition = NEW_BACKGROUND_TAB; | 
 |   params.window_action = chrome::NavigateParams::NO_ACTION; | 
 |   ui_test_utils::NavigateToURL(¶ms); | 
 |   navigation_observer.WaitForNavigations(2); | 
 |  | 
 |   // Make sure the active window hasn't changed, and its new tab is | 
 |   // active. | 
 |   ASSERT_EQ(active_browser, | 
 |             browser::FindTabbedBrowser(browser()->profile(), true)); | 
 |   ASSERT_EQ(1, active_browser->active_index()); | 
 |  | 
 |   // Check that the only two navigated tabs were the new error tab in the | 
 |   // backround windows, and the login tab in the active window. | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    chrome::GetWebContentsAt(inactive_browser, 1))); | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    chrome::GetWebContentsAt(active_browser, 1))); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |  | 
 |   // Check captive portal test results. | 
 |   portal_observer.WaitForResults(1); | 
 |   ASSERT_EQ(RESULT_BEHIND_CAPTIVE_PORTAL, | 
 |             portal_observer.captive_portal_result()); | 
 |   EXPECT_EQ(1, portal_observer.num_results_received()); | 
 |  | 
 |   // Check the inactive browser. | 
 |   EXPECT_EQ(2, inactive_browser->tab_count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(inactive_browser, 0)); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL, | 
 |             GetStateOfTabReloaderAt(inactive_browser, 1)); | 
 |  | 
 |   // Check the active browser. | 
 |   ASSERT_EQ(2, active_browser->tab_count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(active_browser, 0)); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(active_browser, 1)); | 
 |   EXPECT_TRUE(IsLoginTab(chrome::GetTabContentsAt(active_browser, 1))); | 
 |  | 
 |   // Simulate logging in. | 
 |   Login(active_browser, 0, 1); | 
 | } | 
 |  | 
 | // An HTTP page redirects to an HTTPS page loads slowly before timing out.  A | 
 | // captive portal is found, and then the user logs in before the original page | 
 | // times out. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, HttpToHttpsRedirectLogin) { | 
 |   ASSERT_TRUE(test_server()->Start()); | 
 |   SlowLoadBehindCaptivePortal( | 
 |       browser(), | 
 |       true, | 
 |       test_server()->GetURL(CreateServerRedirect(kMockHttpsUrl)), | 
 |       1, | 
 |       1); | 
 |   Login(browser(), 1, 0); | 
 |   FailLoadsAfterLogin(browser(), 1); | 
 | } | 
 |  | 
 | // An HTTPS page redirects to an HTTP page. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, HttpsToHttpRedirect) { | 
 |   // Use an HTTPS server for the top level page. | 
 |   net::TestServer https_server(net::TestServer::TYPE_HTTPS, | 
 |                                net::TestServer::kLocalhost, | 
 |                                FilePath(FILE_PATH_LITERAL("chrome/test/data"))); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |  | 
 |   GURL http_timeout_url = | 
 |       URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_TIMED_OUT); | 
 |  | 
 |   // 2 navigations due to the Link Doctor. | 
 |   NavigateToPageExpectNoTest( | 
 |       browser(), | 
 |       https_server.GetURL(CreateServerRedirect(http_timeout_url.spec())), | 
 |       2); | 
 | } | 
 |  | 
 | // Tests the 511 response code, along with an HTML redirect to a login page. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, Status511) { | 
 |   SetUpCaptivePortalService(browser()->profile(), | 
 |                             GURL(kMockCaptivePortal511Url)); | 
 |   SlowLoadBehindCaptivePortal(browser(), true, GURL(kMockHttpsUrl), 2, 2); | 
 |   Login(browser(), 1, 0); | 
 |   FailLoadsAfterLogin(browser(), 1); | 
 | } | 
 |  | 
 | // HSTS redirects an HTTP request to HTTPS, and the request then times out. | 
 | // A captive portal is then detected, and a login tab opened, before logging | 
 | // in. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, HstsLogin) { | 
 |   GURL::Replacements replacements; | 
 |   std::string scheme = "http"; | 
 |   replacements.SetSchemeStr(scheme); | 
 |   GURL http_timeout_url = GURL(kMockHttpsUrl).ReplaceComponents(replacements); | 
 |  | 
 |   URLRequestFailedJob::GetMockHttpUrl(net::ERR_CONNECTION_TIMED_OUT); | 
 |   content::BrowserThread::PostTask( | 
 |       content::BrowserThread::IO, FROM_HERE, | 
 |       base::Bind(&AddHstsHost, | 
 |                  make_scoped_refptr(browser()->profile()->GetRequestContext()), | 
 |                  http_timeout_url.host())); | 
 |  | 
 |   SlowLoadBehindCaptivePortal(browser(), true, http_timeout_url, 1, 1); | 
 |   Login(browser(), 1, 0); | 
 |   FailLoadsAfterLogin(browser(), 1); | 
 | } | 
 |  | 
 | }  // namespace captive_portal |