| // 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 <algorithm> | 
 | #include <atomic> | 
 | #include <iterator> | 
 | #include <map> | 
 | #include <memory> | 
 | #include <set> | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "base/base_switches.h" | 
 | #include "base/bind.h" | 
 | #include "base/command_line.h" | 
 | #include "base/compiler_specific.h" | 
 | #include "base/feature_list.h" | 
 | #include "base/files/file_path.h" | 
 | #include "base/macros.h" | 
 | #include "base/message_loop/message_loop.h" | 
 | #include "base/path_service.h" | 
 | #include "base/run_loop.h" | 
 | #include "base/scoped_observer.h" | 
 | #include "base/sequence_checker.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "base/task/post_task.h" | 
 | #include "base/test/bind_test_util.h" | 
 | #include "base/values.h" | 
 | #include "build/build_config.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/chrome_notification_types.h" | 
 | #include "chrome/browser/profiles/profile.h" | 
 | #include "chrome/browser/ssl/captive_portal_blocking_page.h" | 
 | #include "chrome/browser/ssl/ssl_blocking_page.h" | 
 | #include "chrome/browser/ssl/ssl_error_handler.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_params.h" | 
 | #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" | 
 | #include "chrome/browser/ui/tabs/tab_strip_model.h" | 
 | #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" | 
 | #include "chrome/common/chrome_features.h" | 
 | #include "chrome/common/chrome_paths.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 "components/prefs/pref_service.h" | 
 | #include "components/security_interstitials/content/security_interstitial_page.h" | 
 | #include "components/security_interstitials/content/security_interstitial_tab_helper.h" | 
 | #include "content/public/browser/browser_task_traits.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 | #include "content/public/browser/interstitial_page.h" | 
 | #include "content/public/browser/interstitial_page_delegate.h" | 
 | #include "content/public/browser/navigation_controller.h" | 
 | #include "content/public/browser/navigation_entry.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_frame_host.h" | 
 | #include "content/public/browser/storage_partition.h" | 
 | #include "content/public/browser/web_contents.h" | 
 | #include "content/public/common/url_constants.h" | 
 | #include "content/public/test/browser_test_utils.h" | 
 | #include "content/public/test/url_loader_interceptor.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "net/cert/x509_certificate.h" | 
 | #include "net/http/transport_security_state.h" | 
 | #include "net/test/cert_test_util.h" | 
 | #include "net/test/embedded_test_server/embedded_test_server.h" | 
 | #include "net/test/test_data_directory.h" | 
 | #include "net/test/url_request/url_request_failed_job.h" | 
 | #include "net/test/url_request/url_request_mock_http_job.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_status.h" | 
 | #include "services/network/public/cpp/features.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | using captive_portal::CaptivePortalResult; | 
 | using content::BrowserThread; | 
 | using content::WebContents; | 
 |  | 
 | namespace { | 
 |  | 
 | // Path of the fake login page, when using the TestServer. | 
 | const char* const kTestServerLoginPath = "/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 = | 
 |     "/captive_portal/iframe_timeout.html"; | 
 |  | 
 | // Path of a page that redirects to kMockHttpsUrl. | 
 | const char* const kRedirectToMockHttpsPath = | 
 |     "/captive_portal/redirect_to_mock_https.html"; | 
 |  | 
 | // Path of a page that serves a bad SSL certificate. | 
 | // The path doesn't matter because all we need is that it's served from a | 
 | // server that's configured to serve a bad cert. | 
 | const char* const kMockHttpsBadCertPath = "/bad_cert.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 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"; | 
 |  | 
 | // The intercepted URLs used to mock errors. | 
 | const char* const kMockHttpConnectionTimeoutErr = | 
 |     "http://mock.captive.portal.quick.error/timeout"; | 
 | const char* const kMockHttpsConnectionTimeoutErr = | 
 |     "https://mock.captive.portal.quick.error/timeout"; | 
 | const char* const kMockHttpsConnectionUnexpectedErr = | 
 |     "https://mock.captive.portal.quick.error/unexpected"; | 
 | const char* const kMockHttpConnectionConnectionClosedErr = | 
 |     "http://mock.captive.portal.quick.error/connection_closed"; | 
 |  | 
 | // Expected title of a tab once an HTTPS load completes, when not behind a | 
 | // captive portal. | 
 | const char* const kInternetConnectedTitle = "Title Of Awesomeness"; | 
 |  | 
 | // 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() { | 
 |   return std::count_if(AllTabContentses().begin(), AllTabContentses().end(), | 
 |                        [](content::WebContents* web_contents) { | 
 |                          return web_contents->IsLoading(); | 
 |                        }); | 
 | } | 
 |  | 
 | bool IsLoginTab(WebContents* web_contents) { | 
 |   return CaptivePortalTabHelper::FromWebContents(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(); | 
 |   ~MultiNavigationObserver() override; | 
 |  | 
 |   // 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(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 WebContents*, int> TabNavigationMap; | 
 |  | 
 |   // content::NotificationObserver: | 
 |   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( | 
 |     WebContents* web_contents) const { | 
 |   auto 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; | 
 |     base::RunLoop::QuitCurrentWhenIdleDeprecated(); | 
 |   } | 
 | } | 
 |  | 
 | // 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(); | 
 |   ~FailLoadsAfterLoginObserver() override; | 
 |  | 
 |   void WaitForNavigations(); | 
 |  | 
 |  private: | 
 |   typedef std::set<const WebContents*> TabSet; | 
 |  | 
 |   // content::NotificationObserver: | 
 |   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()); | 
 |   std::copy_if( | 
 |       AllTabContentses().begin(), AllTabContentses().end(), | 
 |       std::inserter(tabs_needing_navigation_, tabs_needing_navigation_.end()), | 
 |       [](content::WebContents* web_contents) { | 
 |         return web_contents->IsLoading(); | 
 |       }); | 
 | } | 
 |  | 
 | 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(); | 
 |   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() != base::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; | 
 |     base::RunLoop::QuitCurrentWhenIdleDeprecated(); | 
 |   } | 
 | } | 
 |  | 
 | // 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 exactly |update_count| captive portal | 
 |   // results have been received, since the 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_; } | 
 |  | 
 |   CaptivePortalResult 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) override; | 
 |  | 
 |   // 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. | 
 |   CaptivePortalResult 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; | 
 |     base::RunLoop::QuitCurrentWhenIdleDeprecated(); | 
 |   } | 
 | } | 
 |  | 
 | // This observer waits for the SSLErrorHandler to start an interstitial timer | 
 | // for the given web contents. | 
 | class SSLInterstitialTimerObserver { | 
 |  public: | 
 |   explicit SSLInterstitialTimerObserver(content::WebContents* web_contents); | 
 |   ~SSLInterstitialTimerObserver(); | 
 |  | 
 |   // Waits until the interstitial delay timer in SSLErrorHandler is started. | 
 |   void WaitForTimerStarted(); | 
 |  | 
 |  private: | 
 |   void OnTimerStarted(content::WebContents* web_contents); | 
 |  | 
 |   const content::WebContents* web_contents_; | 
 |   SSLErrorHandler::TimerStartedCallback callback_; | 
 |  | 
 |   scoped_refptr<content::MessageLoopRunner> message_loop_runner_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(SSLInterstitialTimerObserver); | 
 | }; | 
 |  | 
 | SSLInterstitialTimerObserver::SSLInterstitialTimerObserver( | 
 |     content::WebContents* web_contents) | 
 |     : web_contents_(web_contents), | 
 |       message_loop_runner_(new content::MessageLoopRunner) { | 
 |   callback_ = base::Bind(&SSLInterstitialTimerObserver::OnTimerStarted, | 
 |                          base::Unretained(this)); | 
 |   SSLErrorHandler::SetInterstitialTimerStartedCallbackForTesting(&callback_); | 
 | } | 
 |  | 
 | SSLInterstitialTimerObserver::~SSLInterstitialTimerObserver() { | 
 |   SSLErrorHandler::SetInterstitialTimerStartedCallbackForTesting(nullptr); | 
 | } | 
 |  | 
 | void SSLInterstitialTimerObserver::WaitForTimerStarted() { | 
 |   message_loop_runner_->Run(); | 
 | } | 
 |  | 
 | void SSLInterstitialTimerObserver::OnTimerStarted( | 
 |     content::WebContents* web_contents) { | 
 |   if (web_contents_ == web_contents && message_loop_runner_.get()) | 
 |     message_loop_runner_->Quit(); | 
 | } | 
 |  | 
 | // Helper for waiting for a change of the active tab. | 
 | // Users can wait for the change via WaitForActiveTabChange method. | 
 | // DCHECKs ensure that only one change happens during the lifetime of a | 
 | // TabActivationWaiter instance. | 
 | class TabActivationWaiter : public TabStripModelObserver { | 
 |  public: | 
 |   explicit TabActivationWaiter(TabStripModel* tab_strip_model) | 
 |       : number_of_unconsumed_active_tab_changes_(0), scoped_observer_(this) { | 
 |     scoped_observer_.Add(tab_strip_model); | 
 |   } | 
 |  | 
 |   void WaitForActiveTabChange() { | 
 |     if (number_of_unconsumed_active_tab_changes_ == 0) { | 
 |       // Wait until TabStripModelObserver::ActiveTabChanged will get called. | 
 |       message_loop_runner_ = new content::MessageLoopRunner; | 
 |       message_loop_runner_->Run(); | 
 |     } | 
 |  | 
 |     // "consume" one tab activation event. | 
 |     DCHECK_EQ(1, number_of_unconsumed_active_tab_changes_); | 
 |     number_of_unconsumed_active_tab_changes_--; | 
 |   } | 
 |  | 
 |   // TabStripModelObserver overrides. | 
 |   void OnTabStripModelChanged( | 
 |       TabStripModel* tab_strip_model, | 
 |       const TabStripModelChange& change, | 
 |       const TabStripSelectionChange& selection) override { | 
 |     if (tab_strip_model->empty() || !selection.active_tab_changed()) | 
 |       return; | 
 |  | 
 |     number_of_unconsumed_active_tab_changes_++; | 
 |     DCHECK_EQ(1, number_of_unconsumed_active_tab_changes_); | 
 |     if (message_loop_runner_) | 
 |       message_loop_runner_->Quit(); | 
 |   } | 
 |  | 
 |  private: | 
 |   scoped_refptr<content::MessageLoopRunner> message_loop_runner_; | 
 |   int number_of_unconsumed_active_tab_changes_; | 
 |   ScopedObserver<TabStripModel, TabActivationWaiter> scoped_observer_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(TabActivationWaiter); | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | class CaptivePortalBrowserTest : public InProcessBrowserTest { | 
 |  public: | 
 |   CaptivePortalBrowserTest(); | 
 |  | 
 |   // InProcessBrowserTest: | 
 |   void SetUpOnMainThread() override; | 
 |   void TearDownOnMainThread() override; | 
 |   void SetUpCommandLine(base::CommandLine* command_line) override; | 
 |  | 
 |   // Called by |url_loader_interceptor_|. | 
 |   // It 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. | 
 |   bool OnIntercept(content::URLLoaderInterceptor::RequestParams* params); | 
 |  | 
 |   // Sets the captive portal checking preference.  Does not affect the command | 
 |   // line flag, which is set in SetUpCommandLine. | 
 |   void EnableCaptivePortalDetection(Profile* profile, bool enabled); | 
 |  | 
 |   // Enables or disables actual captive portal probes. Should only be called | 
 |   // after captive portal service setup is done. When disabled, probe requests | 
 |   // are silently ignored, never receiving a response. | 
 |   void RespondToProbeRequests(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 type of the interstitial being shown. | 
 |   content::InterstitialPageDelegate::TypeID GetInterstitialType( | 
 |       WebContents* contents) const; | 
 |  | 
 |   bool IsShowingInterstitial(WebContents* contents); | 
 |  | 
 |   // Without committed interstitials, this waits for an interstitial to attach | 
 |   // to the current WebContents. With committed interstitials, it instead | 
 |   // asserts an interstitial is showing and waits for the render frame to be | 
 |   // ready. | 
 |   void WaitForInterstitial(content::WebContents* contents); | 
 |  | 
 |   // Returns the CaptivePortalTabReloader::State of |web_contents|. | 
 |   CaptivePortalTabReloader::State GetStateOfTabReloader( | 
 |       WebContents* web_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, | 
 |                                CaptivePortalResult 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, | 
 |                                   CaptivePortalResult 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 FailJobs() is called, at which point it will timeout. | 
 |   // | 
 |   // When |expect_open_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_open_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 WaitForJobs having been called. | 
 |   void SlowLoadBehindCaptivePortal(Browser* browser, | 
 |                                    bool expect_open_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 active tab to an SSL error page which triggers an | 
 |   // interstitial timer. Also disables captive portal checks indefinitely, so | 
 |   // the page appears to be hanging. | 
 |   void FastErrorWithInterstitialTimer(Browser* browser, | 
 |                                       const GURL& cert_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); | 
 |  | 
 |   // Simulates a login when the broken tab shows an SSL or captive portal | 
 |   // interstitial. Can't use Login() in those cases 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). | 
 |   void LoginCertError(Browser* browser); | 
 |  | 
 |   // 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(WebContents* web_contents) const; | 
 |  | 
 |   // Sets whether or not there is a captive portal. Outstanding requests are | 
 |   // not affected. | 
 |   void SetBehindCaptivePortal(bool behind_captive_portal) { | 
 |     behind_captive_portal_ = behind_captive_portal; | 
 |   } | 
 |  | 
 |   // Waits for exactly |num_jobs| kMockHttps* requests. | 
 |   void WaitForJobs(int num_jobs) { | 
 |     if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { | 
 |       base::PostTaskWithTraits( | 
 |           FROM_HERE, {content::BrowserThread::IO}, | 
 |           base::BindOnce(&CaptivePortalBrowserTest::WaitForJobs, | 
 |                          base::Unretained(this), num_jobs)); | 
 |       content::RunMessageLoop(); | 
 |       return; | 
 |     } | 
 |  | 
 |     DCHECK(!num_jobs_to_wait_for_); | 
 |     EXPECT_LE(static_cast<int>(ongoing_mock_requests_.size()), num_jobs); | 
 |     if (num_jobs == static_cast<int>(ongoing_mock_requests_.size())) { | 
 |       base::PostTaskWithTraits( | 
 |           FROM_HERE, {BrowserThread::UI}, | 
 |           base::RunLoop::QuitCurrentWhenIdleClosureDeprecated()); | 
 |     } else { | 
 |       num_jobs_to_wait_for_ = num_jobs; | 
 |     } | 
 |   } | 
 |  | 
 |   // Fails all active kMockHttps* requests with connection timeouts. | 
 |   // There are expected to be exactly |expected_num_jobs| waiting for | 
 |   // failure.  The only way to guarantee this is with an earlier call to | 
 |   // WaitForJobs, so makes sure there has been a matching WaitForJobs call. | 
 |   void FailJobs(int expected_num_jobs) { | 
 |     if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { | 
 |       base::PostTaskWithTraits( | 
 |           FROM_HERE, {BrowserThread::IO}, | 
 |           base::BindOnce(&CaptivePortalBrowserTest::FailJobs, | 
 |                          base::Unretained(this), expected_num_jobs)); | 
 |       return; | 
 |     } | 
 |  | 
 |     EXPECT_EQ(expected_num_jobs, | 
 |               static_cast<int>(ongoing_mock_requests_.size())); | 
 |     network::URLLoaderCompletionStatus status; | 
 |     status.error_code = net::ERR_CONNECTION_TIMED_OUT; | 
 |     for (auto& job : ongoing_mock_requests_) | 
 |       job.client->OnComplete(status); | 
 |     ongoing_mock_requests_.clear(); | 
 |   } | 
 |  | 
 |   // Fails all active kMockHttps* requests with SSL cert errors. | 
 |   // |expected_num_jobs| behaves just as in FailJobs. | 
 |   void FailJobsWithCertError(int expected_num_jobs, | 
 |                              const net::SSLInfo& ssl_info) { | 
 |     if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { | 
 |       base::PostTaskWithTraits( | 
 |           FROM_HERE, {BrowserThread::IO}, | 
 |           base::BindOnce(&CaptivePortalBrowserTest::FailJobsWithCertError, | 
 |                          base::Unretained(this), expected_num_jobs, ssl_info)); | 
 |       return; | 
 |     } | 
 |  | 
 |     DCHECK(intercept_bad_cert_); | 
 |     // With the network service enabled, these will be requests to | 
 |     // kMockHttpsBadCertPath that is served by a misconfigured | 
 |     // EmbeddedTestServer. Once the request reaches the network service, it'll | 
 |     // notice the bad SSL cert. | 
 |     // Set |intercept_bad_cert_| so that when we use the network service' | 
 |     // URLLoaderFactory again it doesn't get intercepted and goes to the | 
 |     // nework process. This has to be done on the UI thread as that's where we | 
 |     // currently have a public URLLoaderFactory for the profile. | 
 |     intercept_bad_cert_ = false; | 
 |     EXPECT_EQ(expected_num_jobs, | 
 |               static_cast<int>(ongoing_mock_requests_.size())); | 
 |     for (auto& job : ongoing_mock_requests_) { | 
 |       base::PostTaskWithTraits( | 
 |           FROM_HERE, {BrowserThread::UI}, | 
 |           base::BindOnce(&CaptivePortalBrowserTest::CreateLoader, | 
 |                          base::Unretained(this), std::move(job))); | 
 |     } | 
 |     ongoing_mock_requests_.clear(); | 
 |   } | 
 |  | 
 |   void CreateLoader(content::URLLoaderInterceptor::RequestParams job) { | 
 |     CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 
 |     content::BrowserContext::GetDefaultStoragePartition(browser()->profile()) | 
 |         ->GetURLLoaderFactoryForBrowserProcess() | 
 |         ->CreateLoaderAndStart(std::move(job.request), job.routing_id, | 
 |                                job.request_id, job.options, | 
 |                                std::move(job.url_request), | 
 |                                std::move(job.client), job.traffic_annotation); | 
 |   } | 
 |  | 
 |   // Abandon all active kMockHttps* requests.  |expected_num_jobs| | 
 |   // behaves just as in FailJobs. | 
 |   void AbandonJobs(int expected_num_jobs) { | 
 |     if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { | 
 |       base::PostTaskWithTraits( | 
 |           FROM_HERE, {BrowserThread::IO}, | 
 |           base::BindOnce(&CaptivePortalBrowserTest::AbandonJobs, | 
 |                          base::Unretained(this), expected_num_jobs)); | 
 |       return; | 
 |     } | 
 |  | 
 |     EXPECT_EQ(expected_num_jobs, | 
 |               static_cast<int>(ongoing_mock_requests_.size())); | 
 |     for (auto& job : ongoing_mock_requests_) | 
 |       ignore_result(job.client.PassInterface().PassHandle().release()); | 
 |     ongoing_mock_requests_.clear(); | 
 |   } | 
 |  | 
 |   // Returns the contents of the given filename under chrome/test/data. | 
 |   static std::string GetContents(const std::string& path) { | 
 |     base::FilePath root_http; | 
 |     base::PathService::Get(chrome::DIR_TEST_DATA, &root_http); | 
 |     base::ScopedAllowBlockingForTesting allow_io; | 
 |     base::FilePath file_path = root_http.AppendASCII(path); | 
 |     std::string contents; | 
 |     CHECK(base::ReadFileToString(file_path, &contents)); | 
 |     return contents; | 
 |   } | 
 |  | 
 |  protected: | 
 |   std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor_; | 
 |   // Only accessed on the IO thread. | 
 |   int num_jobs_to_wait_for_ = 0; | 
 |   std::vector<content::URLLoaderInterceptor::RequestParams> | 
 |       ongoing_mock_requests_; | 
 |   std::atomic<bool> behind_captive_portal_; | 
 |   bool intercept_bad_cert_ = true; | 
 |  | 
 |  private: | 
 |   DISALLOW_COPY_AND_ASSIGN(CaptivePortalBrowserTest); | 
 | }; | 
 |  | 
 | CaptivePortalBrowserTest::CaptivePortalBrowserTest() | 
 |     : behind_captive_portal_(true) {} | 
 |  | 
 | void CaptivePortalBrowserTest::SetUpOnMainThread() { | 
 |   url_loader_interceptor_ = | 
 |       std::make_unique<content::URLLoaderInterceptor>(base::Bind( | 
 |           &CaptivePortalBrowserTest::OnIntercept, base::Unretained(this))); | 
 |  | 
 |   // 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)); | 
 |  | 
 |   // Set SSL interstitial delay long enough so that a captive portal result | 
 |   // is guaranteed to arrive during this window, and a captive portal | 
 |   // error page is displayed instead of an SSL interstitial. | 
 |   SSLErrorHandler::SetInterstitialDelayForTesting( | 
 |       base::TimeDelta::FromHours(1)); | 
 | } | 
 |  | 
 | bool CaptivePortalBrowserTest::OnIntercept( | 
 |     content::URLLoaderInterceptor::RequestParams* params) { | 
 |   if (params->url_request.url.path() == kMockHttpsBadCertPath && | 
 |       intercept_bad_cert_) { | 
 |     CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 
 |     ongoing_mock_requests_.emplace_back(std::move(*params)); | 
 |     return true; | 
 |   } | 
 |  | 
 |   auto url_string = params->url_request.url.spec(); | 
 |   net::Error error = net::OK; | 
 |   if (url_string == kMockHttpConnectionTimeoutErr || | 
 |       url_string == kMockHttpsConnectionTimeoutErr) { | 
 |     error = net::ERR_CONNECTION_TIMED_OUT; | 
 |   } else if (url_string == kMockHttpsConnectionUnexpectedErr) { | 
 |     error = net::ERR_UNEXPECTED; | 
 |   } else if (url_string == kMockHttpConnectionConnectionClosedErr) { | 
 |     error = net::ERR_CONNECTION_CLOSED; | 
 |   } | 
 |   if (error != net::OK) { | 
 |     params->client->OnComplete(network::URLLoaderCompletionStatus(error)); | 
 |     return true; | 
 |   } | 
 |  | 
 |   if (url_string == kMockHttpsUrl || url_string == kMockHttpsUrl2 || | 
 |       url_string == kMockHttpsQuickTimeoutUrl || | 
 |       params->url_request.url.path() == kRedirectToMockHttpsPath) { | 
 |     CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 
 |     if (params->url_request.url.path() == kRedirectToMockHttpsPath) { | 
 |       net::RedirectInfo redirect_info; | 
 |       redirect_info.new_url = GURL(kMockHttpsUrl); | 
 |       redirect_info.new_method = "GET"; | 
 |  | 
 |       std::string headers; | 
 |       headers = base::StringPrintf( | 
 |           "HTTP/1.0 301 Moved permanently\n" | 
 |           "Location: %s\n" | 
 |           "Content-Type: text/html\n\n", | 
 |           kMockHttpsUrl); | 
 |       net::HttpResponseInfo info; | 
 |       info.headers = base::MakeRefCounted<net::HttpResponseHeaders>( | 
 |           net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.length())); | 
 |       network::ResourceResponseHead response; | 
 |       response.headers = info.headers; | 
 |       response.headers->GetMimeType(&response.mime_type); | 
 |       response.encoded_data_length = 0; | 
 |       params->client->OnReceiveRedirect(redirect_info, response); | 
 |     } | 
 |  | 
 |     if (behind_captive_portal_) { | 
 |       if (url_string == kMockHttpsQuickTimeoutUrl) { | 
 |         network::URLLoaderCompletionStatus status; | 
 |         status.error_code = net::ERR_CONNECTION_TIMED_OUT; | 
 |         params->client->OnComplete(status); | 
 |       } else { | 
 |         ongoing_mock_requests_.emplace_back(std::move(*params)); | 
 |         if (num_jobs_to_wait_for_ == | 
 |             static_cast<int>(ongoing_mock_requests_.size())) { | 
 |           num_jobs_to_wait_for_ = 0; | 
 |           base::PostTaskWithTraits( | 
 |               FROM_HERE, {BrowserThread::UI}, | 
 |               base::RunLoop::QuitCurrentWhenIdleClosureDeprecated()); | 
 |         } | 
 |       } | 
 |     } else { | 
 |       // Once logged in to the portal, HTTPS requests return the page that was | 
 |       // actually requested. | 
 |       content::URLLoaderInterceptor::WriteResponse( | 
 |           "HTTP/1.1 200 OK\nContent-type: text/html\n\n", | 
 |           GetContents("title2.html"), params->client.get()); | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   std::string headers; | 
 |   if (url_string == kMockCaptivePortalTestUrl || | 
 |       url_string == kMockCaptivePortal511Url) { | 
 |     std::string contents; | 
 |     if (behind_captive_portal_) { | 
 |       // Prior to logging in to the portal, the HTTP test URLs are | 
 |       // intercepted by the captive portal. | 
 |       if (url_string == kMockCaptivePortal511Url) { | 
 |         contents = GetContents("captive_portal/page511.html"); | 
 |         headers = "HTTP/1.1 511 Network Authentication Required\n"; | 
 |       } else { | 
 |         contents = GetContents("captive_portal/login.html"); | 
 |         headers = "HTTP/1.0 200 Just Peachy\n"; | 
 |       } | 
 |     } else { | 
 |       // After logging in to the portal, the test URLs return a 204 | 
 |       // response. | 
 |       headers = "HTTP/1.0 204 No Content\nContent-Length: 0\n"; | 
 |     } | 
 |     headers += "Content-Type: text/html\n\n"; | 
 |     content::URLLoaderInterceptor::WriteResponse(headers, contents, | 
 |                                                  params->client.get()); | 
 |     return true; | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::TearDownOnMainThread() { | 
 |   // No test should have a captive portal check pending on quit. | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 |   url_loader_interceptor_.reset(); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::SetUpCommandLine( | 
 |     base::CommandLine* command_line) { | 
 |   // Enable finch experiment for captive portal interstitials. | 
 |   command_line->AppendSwitchASCII( | 
 |       switches::kForceFieldTrials, "CaptivePortalInterstitial/Enabled/"); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::EnableCaptivePortalDetection( | 
 |     Profile* profile, bool enabled) { | 
 |   profile->GetPrefs()->SetBoolean(prefs::kAlternateErrorPagesEnabled, enabled); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::RespondToProbeRequests(bool enabled) { | 
 |   if (enabled) { | 
 |     EXPECT_EQ(CaptivePortalService::IGNORE_REQUESTS_FOR_TESTING, | 
 |               CaptivePortalService::get_state_for_testing()); | 
 |     CaptivePortalService::set_state_for_testing( | 
 |         CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING); | 
 |   } else { | 
 |     EXPECT_EQ(CaptivePortalService::SKIP_OS_CHECK_FOR_TESTING, | 
 |               CaptivePortalService::get_state_for_testing()); | 
 |     CaptivePortalService::set_state_for_testing( | 
 |         CaptivePortalService::IGNORE_REQUESTS_FOR_TESTING); | 
 |   } | 
 | } | 
 |  | 
 | 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->DetectionInProgress() || | 
 |       captive_portal_service->TimerRunning(); | 
 | } | 
 |  | 
 | content::InterstitialPageDelegate::TypeID | 
 | CaptivePortalBrowserTest::GetInterstitialType(WebContents* contents) const { | 
 |   if (base::FeatureList::IsEnabled(features::kSSLCommittedInterstitials)) { | 
 |     security_interstitials::SecurityInterstitialTabHelper* helper = | 
 |         security_interstitials::SecurityInterstitialTabHelper::FromWebContents( | 
 |             contents); | 
 |     if (!helper) | 
 |       return nullptr; | 
 |     security_interstitials::SecurityInterstitialPage* blocking_page = | 
 |         helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting(); | 
 |     if (!blocking_page) | 
 |       return nullptr; | 
 |     return blocking_page->GetTypeForTesting(); | 
 |   } | 
 |   if (!contents->ShowingInterstitialPage()) | 
 |     return nullptr; | 
 |   return contents->GetInterstitialPage() | 
 |       ->GetDelegateForTesting() | 
 |       ->GetTypeForTesting(); | 
 | } | 
 |  | 
 | bool CaptivePortalBrowserTest::IsShowingInterstitial(WebContents* contents) { | 
 |   return GetInterstitialType(contents) != nullptr; | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::WaitForInterstitial( | 
 |     content::WebContents* contents) { | 
 |   if (base::FeatureList::IsEnabled(features::kSSLCommittedInterstitials)) { | 
 |     ASSERT_TRUE(IsShowingInterstitial(contents)); | 
 |     ASSERT_TRUE(WaitForRenderFrameReady(contents->GetMainFrame())); | 
 |   } else { | 
 |     content::WaitForInterstitialAttach(contents); | 
 |     ASSERT_TRUE(IsShowingInterstitial(contents)); | 
 |     ASSERT_TRUE(WaitForRenderFrameReady( | 
 |         contents->GetInterstitialPage()->GetMainFrame())); | 
 |   } | 
 | } | 
 |  | 
 | CaptivePortalTabReloader::State CaptivePortalBrowserTest::GetStateOfTabReloader( | 
 |     WebContents* web_contents) const { | 
 |   return GetTabReloader(web_contents)->state(); | 
 | } | 
 |  | 
 | CaptivePortalTabReloader::State | 
 | CaptivePortalBrowserTest::GetStateOfTabReloaderAt(Browser* browser, | 
 |                                                   int index) const { | 
 |   return GetStateOfTabReloader( | 
 |       browser->tab_strip_model()->GetWebContentsAt(index)); | 
 | } | 
 |  | 
 | int CaptivePortalBrowserTest::NumTabsWithState( | 
 |     CaptivePortalTabReloader::State state) const { | 
 |   return std::count_if(AllTabContentses().begin(), AllTabContentses().end(), | 
 |                        [this, state](content::WebContents* web_contents) { | 
 |                          return GetStateOfTabReloader(web_contents) == state; | 
 |                        }); | 
 | } | 
 |  | 
 | 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_strip_model()->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, | 
 |     CaptivePortalResult expected_result) { | 
 |   CaptivePortalTabReloader* tab_reloader = | 
 |       GetTabReloader(browser->tab_strip_model()->GetActiveWebContents()); | 
 |   ASSERT_TRUE(tab_reloader); | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta()); | 
 |  | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |   ui_test_utils::NavigateToURLWithDisposition( | 
 |       browser, GURL(kMockHttpsUrl), WindowOpenDisposition::CURRENT_TAB, | 
 |       ui_test_utils::BROWSER_TEST_NONE); | 
 |  | 
 |   portal_observer.WaitForResults(1); | 
 |  | 
 |   ASSERT_EQ(1, browser->tab_strip_model()->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. | 
 |   WaitForJobs(1); | 
 |   FailJobs(1); | 
 |   navigation_observer.WaitForNavigations(1); | 
 |  | 
 |   ASSERT_EQ(1, browser->tab_strip_model()->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, | 
 |     CaptivePortalResult expected_result) { | 
 |   ASSERT_NE(expected_result, captive_portal::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(browser->tab_strip_model()->GetActiveWebContents()); | 
 |   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->tab_strip_model()->active_index(); | 
 |   int expected_tab_count = browser->tab_strip_model()->count(); | 
 |  | 
 |   ui_test_utils::NavigateToURL(browser, GURL(kMockHttpsConnectionTimeoutErr)); | 
 |  | 
 |   // 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( | 
 |                    browser->tab_strip_model()->GetWebContentsAt(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_strip_model()->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); | 
 |   TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
 |   // Calling this on a tab that's waiting for a load to manually be timed out | 
 |   // will result in a hang. | 
 |   ASSERT_FALSE(tab_strip_model->GetActiveWebContents()->IsLoading()); | 
 |  | 
 |   // Trigger a captive portal check quickly. | 
 |   CaptivePortalTabReloader* tab_reloader = | 
 |       GetTabReloader(tab_strip_model->GetActiveWebContents()); | 
 |   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 = tab_strip_model->count(); | 
 |   int initial_active_index = tab_strip_model->active_index(); | 
 |   int initial_loading_tabs = NumLoadingTabs(); | 
 |   int expected_broken_tabs = NumBrokenTabs(); | 
 |   if (CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL != | 
 |           GetStateOfTabReloader(tab_strip_model->GetActiveWebContents())) { | 
 |     ++expected_broken_tabs; | 
 |   } | 
 |  | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |   ui_test_utils::NavigateToURLWithDisposition( | 
 |       browser, hanging_url, WindowOpenDisposition::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, tab_strip_model->count()); | 
 |     EXPECT_EQ(initial_tab_count, tab_strip_model->active_index()); | 
 |  | 
 |     EXPECT_EQ(expected_login_tab_navigations, | 
 |               navigation_observer.NumNavigationsForTab( | 
 |                   tab_strip_model->GetWebContentsAt(initial_tab_count))); | 
 |     EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |               GetStateOfTabReloaderAt(browser, 1)); | 
 |     EXPECT_TRUE(IsLoginTab(tab_strip_model->GetWebContentsAt(1))); | 
 |   } else { | 
 |     EXPECT_EQ(0, navigation_observer.num_navigations()); | 
 |     EXPECT_EQ(initial_active_index, tab_strip_model->active_index()); | 
 |     ASSERT_EQ(initial_tab_count, tab_strip_model->count()); | 
 |     EXPECT_EQ(initial_active_index, tab_strip_model->active_index()); | 
 |   } | 
 |  | 
 |   // Wait for all the expect resource loads to actually start, so subsequent | 
 |   // functions can rely on them having started. | 
 |   WaitForJobs(initial_loading_tabs + 1); | 
 |  | 
 |   EXPECT_EQ(initial_loading_tabs + 1, NumLoadingTabs()); | 
 |   EXPECT_EQ(expected_broken_tabs, NumBrokenTabs()); | 
 |   EXPECT_EQ(captive_portal::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, | 
 |             GetStateOfTabReloaderAt(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) { | 
 |   TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
 |   // Calling this on a tab that's waiting for a load to manually be timed out | 
 |   // will result in a hang. | 
 |   ASSERT_FALSE(tab_strip_model->GetActiveWebContents()->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(tab_strip_model->GetActiveWebContents()); | 
 |   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 = tab_strip_model->count(); | 
 |   int initial_active_index = tab_strip_model->active_index(); | 
 |   int initial_loading_tabs = NumLoadingTabs(); | 
 |   int expected_broken_tabs = NumBrokenTabs(); | 
 |   if (CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL != | 
 |           GetStateOfTabReloader(tab_strip_model->GetActiveWebContents())) { | 
 |     ++expected_broken_tabs; | 
 |   } | 
 |  | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |   ui_test_utils::NavigateToURLWithDisposition( | 
 |       browser, error_url, WindowOpenDisposition::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, tab_strip_model->count()); | 
 |     EXPECT_EQ(initial_tab_count, tab_strip_model->active_index()); | 
 |     // Make sure that the originally active tab and the captive portal tab have | 
 |     // each loaded once. | 
 |     EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                      tab_strip_model->GetWebContentsAt(initial_active_index))); | 
 |     EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                      tab_strip_model->GetWebContentsAt(initial_tab_count))); | 
 |     EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |               GetStateOfTabReloaderAt(browser, 1)); | 
 |     EXPECT_TRUE(IsLoginTab(tab_strip_model->GetWebContentsAt(1))); | 
 |   } else { | 
 |     navigation_observer.WaitForNavigations(1); | 
 |     EXPECT_EQ(initial_active_index, tab_strip_model->active_index()); | 
 |     EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                      tab_strip_model->GetWebContentsAt(initial_active_index))); | 
 |     ASSERT_EQ(initial_tab_count, tab_strip_model->count()); | 
 |     EXPECT_EQ(initial_active_index, tab_strip_model->active_index()); | 
 |   } | 
 |  | 
 |   EXPECT_EQ(initial_loading_tabs, NumLoadingTabs()); | 
 |   EXPECT_EQ(expected_broken_tabs, NumBrokenTabs()); | 
 |   EXPECT_EQ(captive_portal::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, | 
 |             GetStateOfTabReloaderAt(browser, initial_active_index)); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::FastErrorWithInterstitialTimer( | 
 |     Browser* browser, | 
 |     const GURL& cert_error_url) { | 
 |   TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
 |   WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents(); | 
 |  | 
 |   // Disable captive portal checks indefinitely. | 
 |   RespondToProbeRequests(false); | 
 |  | 
 |   SSLInterstitialTimerObserver interstitial_timer_observer(broken_tab_contents); | 
 |   ui_test_utils::NavigateToURLWithDisposition( | 
 |       browser, cert_error_url, WindowOpenDisposition::CURRENT_TAB, | 
 |       ui_test_utils::BROWSER_TEST_NONE); | 
 |   interstitial_timer_observer.WaitForTimerStarted(); | 
 |  | 
 |   // The tab should be in loading state, waiting for the interstitial timer to | 
 |   // expire or a captive portal result to arrive. Since captive portal checks | 
 |   // are disabled and timer set to expire after a very long time, the tab should | 
 |   // hang indefinitely. | 
 |   EXPECT_TRUE(broken_tab_contents->IsLoading()); | 
 |   EXPECT_EQ(1, NumLoadingTabs()); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::NavigateLoginTab(Browser* browser, | 
 |                                                 int num_loading_tabs, | 
 |                                                 int num_timed_out_tabs) { | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
 |   int initial_tab_count = tab_strip_model->count(); | 
 |   EXPECT_EQ(num_loading_tabs, NumLoadingTabs()); | 
 |   EXPECT_EQ(num_timed_out_tabs, NumBrokenTabs() - NumLoadingTabs()); | 
 |  | 
 |   int login_tab_index = tab_strip_model->active_index(); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloader(tab_strip_model->GetActiveWebContents())); | 
 |   ASSERT_TRUE(IsLoginTab(browser->tab_strip_model()->GetActiveWebContents())); | 
 |  | 
 |   // Do the navigation. | 
 |   content::ExecuteScriptAsync(tab_strip_model->GetActiveWebContents(), | 
 |                               "submitForm()"); | 
 |  | 
 |   portal_observer.WaitForResults(1); | 
 |   navigation_observer.WaitForNavigations(1); | 
 |  | 
 |   // Check the captive portal result. | 
 |   EXPECT_EQ(captive_portal::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, tab_strip_model->count()); | 
 |   EXPECT_EQ(num_loading_tabs, NumLoadingTabs()); | 
 |   EXPECT_EQ(num_loading_tabs + num_timed_out_tabs, NumBrokenTabs()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser, login_tab_index)); | 
 |   EXPECT_TRUE(IsLoginTab(tab_strip_model->GetWebContentsAt(login_tab_index))); | 
 |  | 
 |   // Make sure there were no unexpected navigations. | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    tab_strip_model->GetWebContentsAt(login_tab_index))); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::Login(Browser* browser, | 
 |                                      int num_loading_tabs, | 
 |                                      int num_timed_out_tabs) { | 
 |   // Simulate logging in. | 
 |   SetBehindCaptivePortal(false); | 
 |  | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
 |   int initial_tab_count = tab_strip_model->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 = tab_strip_model->active_index(); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser, login_tab_index)); | 
 |   ASSERT_TRUE(IsLoginTab(tab_strip_model->GetWebContentsAt(login_tab_index))); | 
 |  | 
 |   // Trigger a navigation. | 
 |   content::ExecuteScriptAsync(tab_strip_model->GetActiveWebContents(), | 
 |                               "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, tab_strip_model->count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser, login_tab_index)); | 
 |   EXPECT_FALSE(IsLoginTab(tab_strip_model->GetWebContentsAt(login_tab_index))); | 
 |  | 
 |   // Make sure there were no unexpected navigations of the login tab. | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    tab_strip_model->GetWebContentsAt(login_tab_index))); | 
 | } | 
 |  | 
 | void CaptivePortalBrowserTest::LoginCertError(Browser* browser) { | 
 |   SetBehindCaptivePortal(false); | 
 |  | 
 |   MultiNavigationObserver navigation_observer; | 
 |   CaptivePortalObserver portal_observer(browser->profile()); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
 |  | 
 |   // Verify that the login page is on top. | 
 |   int login_tab_index = tab_strip_model->active_index(); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser, login_tab_index)); | 
 |   ASSERT_TRUE(IsLoginTab(tab_strip_model->GetWebContentsAt(login_tab_index))); | 
 |  | 
 |   // Trigger a navigation. | 
 |   content::ExecuteScriptAsync(tab_strip_model->GetActiveWebContents(), | 
 |                               "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, tab_strip_model->count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser, 0)); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser, login_tab_index)); | 
 |   EXPECT_FALSE(IsLoginTab(tab_strip_model->GetWebContentsAt(login_tab_index))); | 
 |  | 
 |   // Make sure only one navigation was for the login tab. | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    tab_strip_model->GetWebContentsAt(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()); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
 |   int initial_num_tabs = tab_strip_model->count(); | 
 |   int initial_active_tab = tab_strip_model->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. | 
 |   WaitForJobs(num_loading_tabs); | 
 |   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, tab_strip_model->count()); | 
 |  | 
 |   EXPECT_EQ(initial_active_tab, tab_strip_model->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()); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
 |   int initial_num_tabs = tab_strip_model->count(); | 
 |   int login_tab = tab_strip_model->active_index(); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloader(tab_strip_model->GetActiveWebContents())); | 
 |   ASSERT_TRUE(IsLoginTab(tab_strip_model->GetActiveWebContents())); | 
 |  | 
 |   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. | 
 |   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, tab_strip_model->count()); | 
 |  | 
 |   EXPECT_EQ(0, NumNeedReloadTabs()); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |   EXPECT_EQ(num_loading_tabs, NumBrokenTabs()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloader(tab_strip_model->GetActiveWebContents())); | 
 |   EXPECT_TRUE(IsLoginTab(tab_strip_model->GetActiveWebContents())); | 
 |   EXPECT_EQ(login_tab, tab_strip_model->active_index()); | 
 |  | 
 |   EXPECT_EQ(0, navigation_observer.NumNavigationsForTab( | 
 |                    tab_strip_model->GetWebContentsAt(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. | 
 |   SetBehindCaptivePortal(false); | 
 |   NavigateToPageExpectNoTest(browser, starting_url, 1); | 
 |   SetBehindCaptivePortal(true); | 
 |  | 
 |   // Go to the first hanging url. | 
 |   SlowLoadBehindCaptivePortal(browser, true, hanging_url, 1, 1); | 
 |  | 
 |   // Abandon the request. | 
 |   WaitForJobs(1); | 
 |   AbandonJobs(1); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser->tab_strip_model(); | 
 |   CaptivePortalTabReloader* tab_reloader = | 
 |       GetTabReloader(tab_strip_model->GetWebContentsAt(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. | 
 |   tab_strip_model->ActivateTabAt(0, true); | 
 |   browser->OpenURL(content::OpenURLParams(timeout_url, content::Referrer(), | 
 |                                           WindowOpenDisposition::CURRENT_TAB, | 
 |                                           ui::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, | 
 |             GetStateOfTabReloaderAt(browser, 1)); | 
 |   ASSERT_TRUE(IsLoginTab(tab_strip_model->GetWebContentsAt(1))); | 
 |  | 
 |   // Need to make sure the request has been issued before logging in. | 
 |   WaitForJobs(1); | 
 |  | 
 |   // Simulate logging in. | 
 |   tab_strip_model->ActivateTabAt(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( | 
 |     WebContents* web_contents) const { | 
 |   return CaptivePortalTabHelper::FromWebContents(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) { | 
 |   NavigateToPageExpectNoTest(browser(), GURL(kMockHttpConnectionTimeoutErr), 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) { | 
 |   NavigateToPageExpectNoTest(browser(), GURL(kMockHttpsConnectionUnexpectedErr), | 
 |                              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::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); | 
 |   https_server.ServeFilesFromSourceDirectory("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(), | 
 |                             GURL(kMockHttpConnectionConnectionClosedErr)); | 
 |   SlowLoadNoCaptivePortal(browser(), captive_portal::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(), | 
 |                             GURL(kMockHttpConnectionConnectionClosedErr)); | 
 |   FastTimeoutNoCaptivePortal(browser(), captive_portal::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(), captive_portal::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(embedded_test_server()->Start()); | 
 |   SetUpCaptivePortalService(browser()->profile(), | 
 |                             embedded_test_server()->GetURL("/nocontent")); | 
 |   SlowLoadNoCaptivePortal(browser(), captive_portal::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(embedded_test_server()->Start()); | 
 |  | 
 |   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); | 
 |   https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); | 
 |   https_server.ServeFilesFromSourceDirectory("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(), | 
 |                             embedded_test_server()->GetURL( | 
 |                                 CreateServerRedirect(ssl_login_url.spec()))); | 
 |  | 
 |   SlowLoadNoCaptivePortal(browser(), captive_portal::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); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   EXPECT_EQ(1, tab_strip_model->count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |  | 
 |   Login(incognito_browser, 1, 0); | 
 |   FailLoadsAfterLogin(incognito_browser, 1); | 
 |  | 
 |   EXPECT_EQ(1, tab_strip_model->count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |  | 
 |   EXPECT_EQ(0, navigation_observer.NumNavigationsForTab( | 
 |                    tab_strip_model->GetWebContentsAt(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. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, | 
 |                        ShowCaptivePortalInterstitialOnCertError) { | 
 |   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); | 
 |   https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); | 
 |   https_server.ServeFilesFromSourceDirectory("chrome/test/data"); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents(); | 
 |  | 
 |   // The path does not matter. | 
 |   GURL cert_error_url = https_server.GetURL(kTestServerLoginPath); | 
 |   int cert_error_tab_index = tab_strip_model->active_index(); | 
 |   // The interstitial should trigger a captive portal check when it opens, just | 
 |   // like navigating to kMockHttpsQuickTimeoutUrl. | 
 |   FastErrorBehindCaptivePortal(browser(), true, cert_error_url); | 
 |   EXPECT_EQ(CaptivePortalBlockingPage::kTypeForTesting, | 
 |             GetInterstitialType(broken_tab_contents)); | 
 |  | 
 |   // Switch to the interstitial and click the |Connect| button. Should switch | 
 |   // active tab to the captive portal landing page. | 
 |   int login_tab_index = tab_strip_model->active_index(); | 
 |   tab_strip_model->ActivateTabAt(cert_error_tab_index, false); | 
 |   // Wait for the interstitial to load all the JavaScript code. Otherwise, | 
 |   // trying to click on a button will fail. | 
 |   content::RenderFrameHost* rfh; | 
 |   if (base::FeatureList::IsEnabled(features::kSSLCommittedInterstitials)) | 
 |     rfh = broken_tab_contents->GetMainFrame(); | 
 |   else | 
 |     rfh = broken_tab_contents->GetInterstitialPage()->GetMainFrame(); | 
 |   EXPECT_TRUE(WaitForRenderFrameReady(rfh)); | 
 |   const char kClickConnectButtonJS[] = | 
 |       "document.getElementById('primary-button').click();"; | 
 |   { | 
 |     TabActivationWaiter tab_activation_waiter(tab_strip_model); | 
 |     content::ExecuteScriptAsync(rfh, kClickConnectButtonJS); | 
 |     tab_activation_waiter.WaitForActiveTabChange(); | 
 |   } | 
 |   EXPECT_EQ(login_tab_index, tab_strip_model->active_index()); | 
 |  | 
 |   // For completeness, close the login tab and try clicking |Connect| again. | 
 |   // A new login tab should open. | 
 |   EXPECT_EQ(1, login_tab_index); | 
 |   content::WebContentsDestroyedWatcher destroyed_watcher( | 
 |       tab_strip_model->GetActiveWebContents()); | 
 |   EXPECT_TRUE( | 
 |       tab_strip_model->CloseWebContentsAt(tab_strip_model->active_index(), 0)); | 
 |   destroyed_watcher.Wait(); | 
 |   MultiNavigationObserver navigation_observer; | 
 |   content::ExecuteScriptAsync(rfh, kClickConnectButtonJS); | 
 |   navigation_observer.WaitForNavigations(1); | 
 |   EXPECT_EQ(login_tab_index, tab_strip_model->active_index()); | 
 |  | 
 |   LoginCertError(browser()); | 
 |  | 
 |   // Once logged in, broken tab should reload and display the SSL interstitial. | 
 |   WaitForInterstitial(broken_tab_contents); | 
 |   tab_strip_model->ActivateTabAt(cert_error_tab_index, false); | 
 |  | 
 |   EXPECT_EQ(SSLBlockingPage::kTypeForTesting, | 
 |             GetInterstitialType(tab_strip_model->GetActiveWebContents())); | 
 |  | 
 |   // Trigger another captive portal check while the SSL interstitial is showing. | 
 |   // At this point the user is logged in to the captive portal, so the captive | 
 |   // portal interstitial shouldn't get recreated. | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   CaptivePortalService* captive_portal_service = | 
 |       CaptivePortalServiceFactory::GetForProfile(browser()->profile()); | 
 |   captive_portal_service->DetectCaptivePortal(); | 
 |   portal_observer.WaitForResults(1); | 
 |   EXPECT_EQ(SSLBlockingPage::kTypeForTesting, | 
 |             GetInterstitialType(broken_tab_contents)); | 
 |  | 
 |   // A captive portal appears. Trigger a final captive portal check. The | 
 |   // captive portal interstitial should still not get recreated. | 
 |   SetBehindCaptivePortal(true); | 
 |   CaptivePortalObserver final_portal_observer(browser()->profile()); | 
 |   captive_portal_service->DetectCaptivePortal(); | 
 |   final_portal_observer.WaitForResults(1); | 
 |   EXPECT_EQ(SSLBlockingPage::kTypeForTesting, | 
 |             GetInterstitialType(broken_tab_contents)); | 
 | } | 
 |  | 
 | // Tests this scenario: | 
 | // - Portal probe requests are ignored, so that no captive portal result can | 
 | //   arrive. | 
 | // - A cert error triggers an interstitial timer with a very long timeout. | 
 | // - No captive portal results arrive, causing the tab to appear as loading | 
 | //   indefinitely (because probe requests are ignored). | 
 | // - Stopping the page load shouldn't result in any interstitials. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, | 
 |                        InterstitialTimerStopNavigationWhileLoading) { | 
 |   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); | 
 |   https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); | 
 |   https_server.ServeFilesFromSourceDirectory("chrome/test/data"); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |   // The path does not matter. | 
 |   GURL cert_error_url = https_server.GetURL(kTestServerLoginPath); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents(); | 
 |  | 
 |   CaptivePortalObserver portal_observer1(browser()->profile()); | 
 |   FastErrorWithInterstitialTimer(browser(), cert_error_url); | 
 |  | 
 |   // Page appears loading. Stop the navigation. There should be no interstitial. | 
 |   MultiNavigationObserver test_navigation_observer; | 
 |   broken_tab_contents->Stop(); | 
 |   test_navigation_observer.WaitForNavigations(1); | 
 |  | 
 |   // Make sure that the |ssl_error_handler| is deleted if page load is stopped. | 
 |   EXPECT_TRUE(nullptr == SSLErrorHandler::FromWebContents(broken_tab_contents)); | 
 |  | 
 |   EXPECT_FALSE(IsShowingInterstitial(broken_tab_contents)); | 
 |   EXPECT_FALSE(broken_tab_contents->IsLoading()); | 
 |   EXPECT_EQ(0, portal_observer1.num_results_received()); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 |   EXPECT_EQ(1, browser()->tab_strip_model()->count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |  | 
 |   // Re-enable captive portal checks and fire one. The result should be ignored. | 
 |   RespondToProbeRequests(true); | 
 |   CaptivePortalObserver portal_observer2(browser()->profile()); | 
 |   CaptivePortalService* captive_portal_service = | 
 |       CaptivePortalServiceFactory::GetForProfile(browser()->profile()); | 
 |   captive_portal_service->DetectCaptivePortal(); | 
 |   portal_observer2.WaitForResults(1); | 
 |  | 
 |   EXPECT_FALSE(IsShowingInterstitial(broken_tab_contents)); | 
 |   EXPECT_FALSE(broken_tab_contents->IsLoading()); | 
 |   EXPECT_EQ(1, portal_observer2.num_results_received()); | 
 |   EXPECT_EQ(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL, | 
 |             portal_observer2.captive_portal_result()); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 |   EXPECT_EQ(1, browser()->tab_strip_model()->count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 | } | 
 |  | 
 | // Same as above, but instead of stopping, the loading page is reloaded. The end | 
 | // result is the same. (i.e. page load stops, no interstitials shown) | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, | 
 |                        InterstitialTimerReloadWhileLoading) { | 
 |   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); | 
 |   https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); | 
 |   https_server.ServeFilesFromSourceDirectory("chrome/test/data"); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |   // The path does not matter. | 
 |   GURL cert_error_url = https_server.GetURL(kTestServerLoginPath); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents(); | 
 |  | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   FastErrorWithInterstitialTimer(browser(), cert_error_url); | 
 |  | 
 |   // Page appears loading. Reloading it cancels the page load. Since the load is | 
 |   // stopped, no cert error occurs and SSLErrorHandler isn't instantiated. | 
 |   MultiNavigationObserver test_navigation_observer; | 
 |   chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB); | 
 |   test_navigation_observer.WaitForNavigations(1); | 
 |  | 
 |   // Make sure that the |ssl_error_handler| is deleted. | 
 |   EXPECT_TRUE(nullptr == SSLErrorHandler::FromWebContents(broken_tab_contents)); | 
 |  | 
 |   EXPECT_FALSE(IsShowingInterstitial(broken_tab_contents)); | 
 |   EXPECT_FALSE(broken_tab_contents->IsLoading()); | 
 |   EXPECT_EQ(0, portal_observer.num_results_received()); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 |   EXPECT_EQ(1, browser()->tab_strip_model()->count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |  | 
 |   // Re-enable captive portal checks and fire one. The result should be ignored. | 
 |   RespondToProbeRequests(true); | 
 |   CaptivePortalObserver portal_observer2(browser()->profile()); | 
 |   CaptivePortalService* captive_portal_service = | 
 |       CaptivePortalServiceFactory::GetForProfile(browser()->profile()); | 
 |   captive_portal_service->DetectCaptivePortal(); | 
 |   portal_observer2.WaitForResults(1); | 
 |  | 
 |   EXPECT_FALSE(IsShowingInterstitial(broken_tab_contents)); | 
 |   EXPECT_FALSE(broken_tab_contents->IsLoading()); | 
 |   EXPECT_EQ(1, portal_observer2.num_results_received()); | 
 |   EXPECT_EQ(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL, | 
 |             portal_observer2.captive_portal_result()); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 |   EXPECT_EQ(1, browser()->tab_strip_model()->count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 | } | 
 |  | 
 | // Same as |InterstitialTimerReloadWhileLoading_NoSSLError|, but instead of | 
 | // reloading, the page is navigated away. The new page should load, and no | 
 | // interstitials should be shown. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, | 
 |                        InterstitialTimerNavigateAwayWhileLoading) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); | 
 |   https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); | 
 |   https_server.ServeFilesFromSourceDirectory("chrome/test/data"); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |   // The path does not matter. | 
 |   GURL cert_error_url = https_server.GetURL(kTestServerLoginPath); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents(); | 
 |  | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   FastErrorWithInterstitialTimer(browser(), cert_error_url); | 
 |  | 
 |   // Page appears loading. Navigating away shouldn't result in any interstitial. | 
 |   // Can't use ui_test_utils::NavigateToURLWithDisposition because it waits for | 
 |   // a load stop notification before starting a new navigation. | 
 |   MultiNavigationObserver test_navigation_observer; | 
 |   browser()->OpenURL(content::OpenURLParams( | 
 |       embedded_test_server()->GetURL("/title2.html"), content::Referrer(), | 
 |       WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false)); | 
 |   test_navigation_observer.WaitForNavigations(1); | 
 |  | 
 |   // Make sure that the |ssl_error_handler| is deleted. | 
 |   EXPECT_TRUE(nullptr == SSLErrorHandler::FromWebContents(broken_tab_contents)); | 
 |  | 
 |   EXPECT_FALSE(IsShowingInterstitial(broken_tab_contents)); | 
 |   EXPECT_FALSE(broken_tab_contents->IsLoading()); | 
 |   EXPECT_EQ(0, portal_observer.num_results_received()); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 |   EXPECT_EQ(1, browser()->tab_strip_model()->count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |  | 
 |   // Re-enable captive portal checks and fire one. The result should be ignored. | 
 |   RespondToProbeRequests(true); | 
 |   CaptivePortalObserver portal_observer2(browser()->profile()); | 
 |   CaptivePortalService* captive_portal_service = | 
 |       CaptivePortalServiceFactory::GetForProfile(browser()->profile()); | 
 |   captive_portal_service->DetectCaptivePortal(); | 
 |   portal_observer2.WaitForResults(1); | 
 |  | 
 |   EXPECT_FALSE(IsShowingInterstitial(broken_tab_contents)); | 
 |   EXPECT_FALSE(broken_tab_contents->IsLoading()); | 
 |   EXPECT_EQ(1, portal_observer2.num_results_received()); | 
 |   EXPECT_EQ(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL, | 
 |             portal_observer2.captive_portal_result()); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 |   EXPECT_EQ(1, browser()->tab_strip_model()->count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 | } | 
 |  | 
 | // Same as above, but the hanging load is interrupted by a navigation to the | 
 | // same page, this time committing the navigation. This should end up with an | 
 | // SSL interstitial when not behind a captive portal. This ensures that a new | 
 | // |SSLErrorHandler| is created on a new navigation, even though the tab's | 
 | // WebContents doesn't change. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     CaptivePortalBrowserTest, | 
 |     InterstitialTimerNavigateWhileLoading_EndWithSSLInterstitial) { | 
 |   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); | 
 |   https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); | 
 |   https_server.ServeFilesFromSourceDirectory("chrome/test/data"); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |   // The path does not matter. | 
 |   GURL cert_error_url = https_server.GetURL(kTestServerLoginPath); | 
 |   SetBehindCaptivePortal(false); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents(); | 
 |  | 
 |   FastErrorWithInterstitialTimer(browser(), cert_error_url); | 
 |   // Page appears loading. Turn on response to probe request again, and navigate | 
 |   // to the same page. This should result in a cert error which should | 
 |   // instantiate an |SSLErrorHandler| and end up showing an SSL interstitial. | 
 |   RespondToProbeRequests(true); | 
 |   // Can't have ui_test_utils do the navigation because it will wait for loading | 
 |   // tabs to stop loading before navigating. | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   MultiNavigationObserver test_navigation_observer; | 
 |   browser()->OpenURL(content::OpenURLParams(cert_error_url, content::Referrer(), | 
 |                                             WindowOpenDisposition::CURRENT_TAB, | 
 |                                             ui::PAGE_TRANSITION_TYPED, false)); | 
 |   test_navigation_observer.WaitForNavigations(1); | 
 |   // Should end up with an SSL interstitial. | 
 |   WaitForInterstitial(broken_tab_contents); | 
 |   ASSERT_TRUE(IsShowingInterstitial(broken_tab_contents)); | 
 |   EXPECT_EQ(SSLBlockingPage::kTypeForTesting, | 
 |             GetInterstitialType(broken_tab_contents)); | 
 |   EXPECT_FALSE(broken_tab_contents->IsLoading()); | 
 |   EXPECT_EQ(1, portal_observer.num_results_received()); | 
 |   EXPECT_EQ(captive_portal::RESULT_INTERNET_CONNECTED, | 
 |             portal_observer.captive_portal_result()); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 |   EXPECT_EQ(1, browser()->tab_strip_model()->count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 | } | 
 |  | 
 | // Same as above, but this time behind a captive portal. | 
 | IN_PROC_BROWSER_TEST_F( | 
 |     CaptivePortalBrowserTest, | 
 |     InterstitialTimerNavigateWhileLoading_EndWithCaptivePortalInterstitial) { | 
 |   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); | 
 |   https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); | 
 |   https_server.ServeFilesFromSourceDirectory("chrome/test/data"); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |   // The path does not matter. | 
 |   GURL cert_error_url = https_server.GetURL(kTestServerLoginPath); | 
 |   SetBehindCaptivePortal(true); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents(); | 
 |   int initial_tab_count = tab_strip_model->count(); | 
 |  | 
 |   FastErrorWithInterstitialTimer(browser(), cert_error_url); | 
 |   // Page appears loading. Turn on response to probe request again, and navigate | 
 |   // to the same page. This should result in a cert error which should | 
 |   // instantiate an |SSLErrorHandler| and end up showing an SSL. | 
 |   RespondToProbeRequests(true); | 
 |   // Can't have ui_test_utils do the navigation because it will wait for loading | 
 |   // tabs to stop loading before navigating. | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   MultiNavigationObserver test_navigation_observer; | 
 |   browser()->OpenURL(content::OpenURLParams(cert_error_url, content::Referrer(), | 
 |                                             WindowOpenDisposition::CURRENT_TAB, | 
 |                                             ui::PAGE_TRANSITION_TYPED, false)); | 
 |   // Expect two navigations: | 
 |   // 1- For completing the load of the above navigation. | 
 |   // 2- For completing the load of the login tab. | 
 |   test_navigation_observer.WaitForNavigations(2); | 
 |   // Should end up with a captive portal interstitial and a new login tab. | 
 |   WaitForInterstitial(broken_tab_contents); | 
 |   ASSERT_TRUE(IsShowingInterstitial(broken_tab_contents)); | 
 |   EXPECT_EQ(CaptivePortalBlockingPage::kTypeForTesting, | 
 |             GetInterstitialType(broken_tab_contents)); | 
 |   ASSERT_EQ(initial_tab_count + 1, tab_strip_model->count()); | 
 |   EXPECT_EQ(initial_tab_count, tab_strip_model->active_index()); | 
 |   EXPECT_FALSE(broken_tab_contents->IsLoading()); | 
 |   EXPECT_EQ(1, portal_observer.num_results_received()); | 
 |   EXPECT_EQ(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL, | 
 |             portal_observer.captive_portal_result()); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |   EXPECT_FALSE(CheckPending(browser())); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 1)); | 
 |   EXPECT_TRUE(IsLoginTab(tab_strip_model->GetWebContentsAt(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(embedded_test_server()->Start()); | 
 |  | 
 |   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); | 
 |   https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); | 
 |   https_server.ServeFilesFromSourceDirectory("chrome/test/data"); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |  | 
 |   // Set SSL interstitial delay to zero so that a captive portal result can not | 
 |   // arrive during this window, so an SSL interstitial is displayed instead | 
 |   // of a captive portal error page. | 
 |   SSLErrorHandler::SetInterstitialDelayForTesting(base::TimeDelta()); | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents(); | 
 |  | 
 |   // The path does not matter. | 
 |   GURL cert_error_url = https_server.GetURL(kTestServerLoginPath); | 
 |   // A captive portal check is triggered in FastErrorBehindCaptivePortal. | 
 |   FastErrorBehindCaptivePortal(browser(), true, cert_error_url); | 
 |  | 
 |   EXPECT_EQ(SSLBlockingPage::kTypeForTesting, | 
 |             GetInterstitialType(broken_tab_contents)); | 
 |  | 
 |   LoginCertError(browser()); | 
 | } | 
 |  | 
 | // 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. | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   tab_strip_model->ActivateTabAt(0, true); | 
 |   FastTimeoutBehindCaptivePortal(browser(), false); | 
 |  | 
 |   // Activate and navigate the captive portal tab.  This should not trigger a | 
 |   // reload of the tab with the error. | 
 |   tab_strip_model->ActivateTabAt(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) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   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(), embedded_test_server()->GetURL("/title2.html"), | 
 |       WindowOpenDisposition::NEW_FOREGROUND_TAB, | 
 |       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   ASSERT_EQ(3, tab_strip_model->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( | 
 |                    tab_strip_model->GetWebContentsAt(2))); | 
 |   ASSERT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 1)); | 
 |   ASSERT_TRUE(IsLoginTab(tab_strip_model->GetWebContentsAt(1))); | 
 |   ASSERT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 2)); | 
 |   ASSERT_EQ(2, tab_strip_model->active_index()); | 
 |  | 
 |   SlowLoadBehindCaptivePortal(browser(), false); | 
 |  | 
 |   tab_strip_model->ActivateTabAt(1, true); | 
 |   Login(browser(), 2, 0); | 
 |   FailLoadsAfterLogin(browser(), 2); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, AbortLoad) { | 
 |   SlowLoadBehindCaptivePortal(browser(), true); | 
 |  | 
 |   // Abandon the request. | 
 |   WaitForJobs(1); | 
 |   AbandonJobs(1); | 
 |  | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   MultiNavigationObserver navigation_observer; | 
 |  | 
 |   // Switch back to the hung tab from the login tab, and abort the navigation. | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   tab_strip_model->ActivateTabAt(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)); | 
 |  | 
 |   tab_strip_model->ActivateTabAt(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) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   // Go to the error page. | 
 |   SlowLoadBehindCaptivePortal(browser(), true); | 
 |   FailLoadsWithoutLogin(browser(), 1); | 
 |  | 
 |   // Navigate the error tab to a non-error page. | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   tab_strip_model->ActivateTabAt(0, true); | 
 |   ui_test_utils::NavigateToURL(browser(), | 
 |                                embedded_test_server()->GetURL("/title2.html")); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |  | 
 |   // Simulate logging in. | 
 |   tab_strip_model->ActivateTabAt(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)); | 
 | } | 
 |  | 
 | // Fails on Windows only, mostly on Win7. http://crbug.com/170033 | 
 | #if defined(OS_WIN) | 
 | #define MAYBE_NavigateLoadingTabToTimeoutTwoSites \ | 
 |         DISABLED_NavigateLoadingTabToTimeoutTwoSites | 
 | #else | 
 | #define MAYBE_NavigateLoadingTabToTimeoutTwoSites \ | 
 |         NavigateLoadingTabToTimeoutTwoSites | 
 | #endif | 
 |  | 
 | // 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, | 
 |                        MAYBE_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) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   RunNavigateLoadingTabToTimeoutTest( | 
 |       browser(), embedded_test_server()->GetURL("/title1.html"), | 
 |       GURL(kMockHttpsUrl), GURL(kMockHttpsUrl2)); | 
 | } | 
 |  | 
 | // Checks that navigating a timed out tab back clears its state. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, GoBack) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   // Navigate to a working page. | 
 |   ui_test_utils::NavigateToURL(browser(), | 
 |                                embedded_test_server()->GetURL("/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. | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   tab_strip_model->ActivateTabAt(0, true); | 
 |   chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB); | 
 |   navigation_observer.WaitForNavigations(1); | 
 |  | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    tab_strip_model->GetWebContentsAt(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) { | 
 |   ASSERT_TRUE(embedded_test_server()->Start()); | 
 |   // Disable captive portal detection so the first navigation doesn't open a | 
 |   // login tab. | 
 |   EnableCaptivePortalDetection(browser()->profile(), false); | 
 |  | 
 |   SlowLoadNoCaptivePortal(browser(), captive_portal::RESULT_INTERNET_CONNECTED); | 
 |  | 
 |   // Navigate to a working page. | 
 |   ui_test_utils::NavigateToURL(browser(), | 
 |                                embedded_test_server()->GetURL("/title2.html")); | 
 |   ASSERT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |  | 
 |   EnableCaptivePortalDetection(browser()->profile(), true); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   CaptivePortalTabReloader* tab_reloader = | 
 |       GetTabReloader(tab_strip_model->GetActiveWebContents()); | 
 |   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(), WindowOpenDisposition::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. | 
 |   WaitForJobs(1); | 
 |  | 
 |   EXPECT_EQ(1, portal_observer.num_results_received()); | 
 |   ASSERT_FALSE(CheckPending(browser())); | 
 |   ASSERT_EQ(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL, | 
 |             portal_observer.captive_portal_result()); | 
 |  | 
 |   ASSERT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 1)); | 
 |   ASSERT_TRUE(IsLoginTab(browser()->tab_strip_model()->GetWebContentsAt(1))); | 
 |  | 
 |   ASSERT_EQ(2, tab_strip_model->count()); | 
 |   EXPECT_EQ(1, tab_strip_model->active_index()); | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    tab_strip_model->GetWebContentsAt(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) { | 
 |   SetBehindCaptivePortal(false); | 
 |  | 
 |   // Do the first navigation while not behind a captive portal. | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   ui_test_utils::NavigateToURL(browser(), GURL(kMockHttpsUrl)); | 
 |   ASSERT_EQ(0, portal_observer.num_results_received()); | 
 |   ASSERT_EQ(1, tab_strip_model->count()); | 
 |  | 
 |   // A captive portal spontaneously appears. | 
 |   SetBehindCaptivePortal(true); | 
 |  | 
 |   CaptivePortalTabReloader* tab_reloader = | 
 |       GetTabReloader(tab_strip_model->GetActiveWebContents()); | 
 |   ASSERT_TRUE(tab_reloader); | 
 |   SetSlowSSLLoadTime(tab_reloader, base::TimeDelta()); | 
 |  | 
 |   MultiNavigationObserver navigation_observer; | 
 |   tab_strip_model->GetActiveWebContents()->GetController().Reload( | 
 |       content::ReloadType::NORMAL, 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. | 
 |   WaitForJobs(1); | 
 |  | 
 |   ASSERT_EQ(1, portal_observer.num_results_received()); | 
 |   ASSERT_FALSE(CheckPending(browser())); | 
 |   ASSERT_EQ(captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL, | 
 |             portal_observer.captive_portal_result()); | 
 |  | 
 |   ASSERT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL, | 
 |             GetStateOfTabReloaderAt(browser(), 0)); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(browser(), 1)); | 
 |   ASSERT_TRUE(IsLoginTab(tab_strip_model->GetWebContentsAt(1))); | 
 |  | 
 |   ASSERT_EQ(2, tab_strip_model->count()); | 
 |   EXPECT_EQ(1, tab_strip_model->active_index()); | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    tab_strip_model->GetWebContentsAt(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(), true)); | 
 |   // Navigate the new browser window so it'll be shown and we can pick the | 
 |   // active window. | 
 |   ui_test_utils::NavigateToURL(browser2, GURL(url::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 = | 
 |       chrome::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 | 
 |   // NavigateParams and NEW_BACKGROUND_TAB to avoid activating the window. | 
 |   NavigateParams params(inactive_browser, GURL(kMockHttpsQuickTimeoutUrl), | 
 |                         ui::PAGE_TRANSITION_TYPED); | 
 |   params.disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB; | 
 |   params.window_action = 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, | 
 |             chrome::FindTabbedBrowser(browser()->profile(), true)); | 
 |   ASSERT_EQ(1, active_browser->tab_strip_model()->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( | 
 |                    inactive_browser->tab_strip_model()->GetWebContentsAt(1))); | 
 |   EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( | 
 |                    active_browser->tab_strip_model()->GetWebContentsAt(1))); | 
 |   EXPECT_EQ(0, NumLoadingTabs()); | 
 |  | 
 |   // Check captive portal test results. | 
 |   portal_observer.WaitForResults(1); | 
 |   ASSERT_EQ(captive_portal::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_strip_model()->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_strip_model()->count()); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(active_browser, 0)); | 
 |   EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |             GetStateOfTabReloaderAt(active_browser, 1)); | 
 |   EXPECT_TRUE( | 
 |       IsLoginTab(active_browser->tab_strip_model()->GetWebContentsAt(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(embedded_test_server()->Start()); | 
 |   SlowLoadBehindCaptivePortal( | 
 |       browser(), true, embedded_test_server()->GetURL(kRedirectToMockHttpsPath), | 
 |       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::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); | 
 |   https_server.AddDefaultHandlers( | 
 |       base::FilePath(FILE_PATH_LITERAL("chrome/test/data"))); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |  | 
 |   // The redirect points to a non-existant host, instead of using a | 
 |   // http://mock.failed.request URL, because with the network service enabled if | 
 |   // the initial URL doesn't go through URLLoaderInterceptor (because it's | 
 |   // served by the EmbeddedTestServer), then URLLoaderInterceptor (which is what | 
 |   // handles mock.failed.request URLs) wouldn't see the redirect. | 
 |   GURL http_error_url("http://doesnt.exist/"); | 
 |   NavigateToPageExpectNoTest( | 
 |       browser(), | 
 |       https_server.GetURL(CreateServerRedirect(http_error_url.spec())), 1); | 
 | } | 
 |  | 
 | // 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); | 
 | } | 
 |  | 
 | // A slow SSL load starts. The reloader triggers a captive portal check, finds a | 
 | // captive portal. The SSL commits with a cert error, triggering another captive | 
 | // portal check. | 
 | // The second check finds no captive portal. The reloader triggers a reload at | 
 | // the same time SSL error handler tries to show an interstitial. Should result | 
 | // in an SSL interstitial. | 
 | IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, | 
 |                        InterstitialTimerCertErrorAfterSlowLoad) { | 
 |   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); | 
 |  | 
 |   GURL cert_error_url; | 
 |   // With the network service, the request must be handled in the network | 
 |   // process as that's what triggers the NetworkServiceClient methods that | 
 |   // call out to SSLManager. | 
 |   https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME); | 
 |   https_server.ServeFilesFromSourceDirectory("chrome/test/data"); | 
 |   ASSERT_TRUE(https_server.Start()); | 
 |   cert_error_url = https_server.GetURL(kMockHttpsBadCertPath); | 
 |  | 
 |   TabStripModel* tab_strip_model = browser()->tab_strip_model(); | 
 |   int broken_tab_index = tab_strip_model->active_index(); | 
 |   WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents(); | 
 |   SlowLoadBehindCaptivePortal(browser(), true, cert_error_url, 1, 1); | 
 |  | 
 |   // No longer behind a captive portal. Committing the SSL page should trigger | 
 |   // an SSL interstitial which triggers a new captive portal check. Since there | 
 |   // is no captive portal anymore, should end up with an SSL interstitial. | 
 |   SetBehindCaptivePortal(false); | 
 |  | 
 |   CaptivePortalObserver portal_observer(browser()->profile()); | 
 |   MultiNavigationObserver navigation_observer; | 
 |   net::SSLInfo info; | 
 |   info.cert_status = net::CERT_STATUS_COMMON_NAME_INVALID; | 
 |   info.cert = | 
 |       net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem"); | 
 |   info.unverified_cert = info.cert; | 
 |   FailJobsWithCertError(1, info); | 
 |   navigation_observer.WaitForNavigations(1); | 
 |   if (base::FeatureList::IsEnabled(features::kSSLCommittedInterstitials)) { | 
 |     // With committed interstitials, the SSL interstitial navigation will result | 
 |     // in the captive portal check firing (and returning no captive portal), so | 
 |     // the state will get reset to none. | 
 |     EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, | 
 |               GetStateOfTabReloaderAt(browser(), broken_tab_index)); | 
 |     WaitForInterstitial(broken_tab_contents); | 
 |     portal_observer.WaitForResults(2); | 
 |   } else { | 
 |     EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD, | 
 |               GetStateOfTabReloaderAt(browser(), broken_tab_index)); | 
 |     WaitForInterstitial(broken_tab_contents); | 
 |     portal_observer.WaitForResults(1); | 
 |   } | 
 |  | 
 |   EXPECT_EQ(SSLBlockingPage::kTypeForTesting, | 
 |             GetInterstitialType(broken_tab_contents)); | 
 | } |