| Index: chrome/browser/captive_portal/captive_portal_browsertest.cc
|
| diff --git a/chrome/browser/captive_portal/captive_portal_browsertest.cc b/chrome/browser/captive_portal/captive_portal_browsertest.cc
|
| index 2a8749ec8fd09f1f53dd64776238fa4d0ede991b..35c2dc8b59a312c42872074ee9a65bfa1e65ef7d 100644
|
| --- a/chrome/browser/captive_portal/captive_portal_browsertest.cc
|
| +++ b/chrome/browser/captive_portal/captive_portal_browsertest.cc
|
| @@ -19,8 +19,11 @@
|
| #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/interstitials/security_interstitial_page.h"
|
| #include "chrome/browser/net/url_request_mock_util.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/ui/browser.h"
|
| #include "chrome/browser/ui/browser_commands.h"
|
| #include "chrome/browser/ui/browser_finder.h"
|
| @@ -33,14 +36,19 @@
|
| #include "chrome/test/base/in_process_browser_test.h"
|
| #include "chrome/test/base/ui_test_utils.h"
|
| #include "content/public/browser/browser_thread.h"
|
| +#include "content/public/browser/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/render_view_host.h"
|
| #include "content/public/browser/web_contents.h"
|
| #include "content/public/common/url_constants.h"
|
| +#include "content/public/test/browser_test_utils.h"
|
| #include "net/base/net_errors.h"
|
| #include "net/http/transport_security_state.h"
|
| #include "net/test/url_request/url_request_failed_job.h"
|
| @@ -109,6 +117,22 @@ const char* const kMockHttpsQuickTimeoutUrl =
|
| // captive portal.
|
| const char* const kInternetConnectedTitle = "Title Of Awesomeness";
|
|
|
| +// Wait until all <script> tags have executed, including jstemplate.
|
| +// This isn't ideal, but the same trick is used in
|
| +// SafeBrowsingBlockingPageBrowserTest to wait for the interstitials.
|
| +bool WaitForPageReady(content::RenderViewHost* rvh) {
|
| + if (!rvh)
|
| + return false;
|
| + std::string ready_state;
|
| + do {
|
| + scoped_ptr<base::Value> value = content::ExecuteScriptAndGetValue(
|
| + rvh->GetMainFrame(), "document.readyState");
|
| + if (!value.get() || !value->GetAsString(&ready_state))
|
| + return false;
|
| + } while (ready_state != "complete");
|
| + return true;
|
| +}
|
| +
|
| // A URL request job that hangs until FailJobs() is called. Started jobs
|
| // are stored in a static class variable containing a linked list so that
|
| // FailJobs() can locate them.
|
| @@ -125,10 +149,14 @@ class URLRequestTimeoutOnDemandJob : public net::URLRequestJob,
|
|
|
| // Fails all active URLRequestTimeoutOnDemandJobs with connection timeouts.
|
| // There are expected to be exactly |expected_num_jobs| waiting for
|
| - // failure. The only way to gaurantee this is with an earlier call to
|
| + // failure. The only way to guarantee this is with an earlier call to
|
| // WaitForJobs, so makes sure there has been a matching WaitForJobs call.
|
| static void FailJobs(int expected_num_jobs);
|
|
|
| + // Fails all active URLRequestTimeoutOnDemandJobs with SSL cert errors.
|
| + // |expected_num_jobs| behaves just as in FailJobs.
|
| + static void FailJobsWithCertError(int expected_num_jobs);
|
| +
|
| // Abandon all active URLRequestTimeoutOnDemandJobs. |expected_num_jobs|
|
| // behaves just as in FailJobs.
|
| static void AbandonJobs(int expected_num_jobs);
|
| @@ -140,6 +168,7 @@ class URLRequestTimeoutOnDemandJob : public net::URLRequestJob,
|
| enum EndJobOperation {
|
| FAIL_JOBS,
|
| ABANDON_JOBS,
|
| + FAIL_JOBS_WITH_CERT_ERROR
|
| };
|
|
|
| URLRequestTimeoutOnDemandJob(net::URLRequest* request,
|
| @@ -226,6 +255,16 @@ void URLRequestTimeoutOnDemandJob::FailJobs(int expected_num_jobs) {
|
| }
|
|
|
| // static
|
| +void URLRequestTimeoutOnDemandJob::FailJobsWithCertError(
|
| + int expected_num_jobs) {
|
| + content::BrowserThread::PostTask(
|
| + content::BrowserThread::IO, FROM_HERE,
|
| + base::Bind(&URLRequestTimeoutOnDemandJob::FailOrAbandonJobsOnIOThread,
|
| + expected_num_jobs,
|
| + FAIL_JOBS_WITH_CERT_ERROR));
|
| +}
|
| +
|
| +// static
|
| void URLRequestTimeoutOnDemandJob::AbandonJobs(int expected_num_jobs) {
|
| content::BrowserThread::PostTask(
|
| content::BrowserThread::IO, FROM_HERE,
|
| @@ -313,6 +352,13 @@ void URLRequestTimeoutOnDemandJob::FailOrAbandonJobsOnIOThread(
|
| job->NotifyStartError(net::URLRequestStatus(
|
| net::URLRequestStatus::FAILED,
|
| net::ERR_CONNECTION_TIMED_OUT));
|
| + } else if (end_job_operation == FAIL_JOBS_WITH_CERT_ERROR) {
|
| + DCHECK(job->request()->url().SchemeIs(url::kHttpsScheme));
|
| + net::SSLInfo info;
|
| + info.cert_status = net::CERT_STATUS_COMMON_NAME_INVALID;
|
| + info.cert = new net::X509Certificate(
|
| + "bad.host", "CA", base::Time::Max(), base::Time::Max());
|
| + job->NotifySSLCertificateError(info, true);
|
| }
|
| }
|
|
|
| @@ -689,8 +735,8 @@ class CaptivePortalObserver : public content::NotificationObserver {
|
| public:
|
| explicit CaptivePortalObserver(Profile* profile);
|
|
|
| - // Runs the message loop until until at exactly |update_count| capitive portal
|
| - // results have been received, since this creation of |this|. Expects no
|
| + // 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);
|
|
|
| @@ -816,6 +862,9 @@ class CaptivePortalBrowserTest : public InProcessBrowserTest {
|
| // check.
|
| bool CheckPending(Browser* browser);
|
|
|
| + // Returns the type of the interstitial being shown.
|
| + const void* GetInterstitialType(WebContents* contents) const;
|
| +
|
| // Returns the CaptivePortalTabReloader::State of |web_contents|.
|
| CaptivePortalTabReloader::State GetStateOfTabReloader(
|
| WebContents* web_contents) const;
|
| @@ -897,9 +946,17 @@ class CaptivePortalBrowserTest : public InProcessBrowserTest {
|
| // 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);
|
| + void FastErrorBehindCaptivePortal(
|
| + Browser* browser,
|
| + bool expect_open_login_tab,
|
| + const GURL& error_url,
|
| + bool disable_portal_check_until_interstitial);
|
| +
|
| + // 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.
|
| @@ -916,6 +973,12 @@ class CaptivePortalBrowserTest : public InProcessBrowserTest {
|
| // 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.
|
| @@ -949,6 +1012,9 @@ class CaptivePortalBrowserTest : public InProcessBrowserTest {
|
| void SetSlowSSLLoadTime(CaptivePortalTabReloader* tab_reloader,
|
| base::TimeDelta slow_ssl_load_time);
|
|
|
| + void SetSSLErrorDisplayDelay(CaptivePortalTabHelper* tab_helper,
|
| + base::TimeDelta ssl_error_delay);
|
| +
|
| CaptivePortalTabReloader* GetTabReloader(WebContents* web_contents) const;
|
|
|
| private:
|
| @@ -995,6 +1061,7 @@ void CaptivePortalBrowserTest::SetUpCaptivePortalService(Profile* profile,
|
| CaptivePortalService* captive_portal_service =
|
| CaptivePortalServiceFactory::GetForProfile(profile);
|
| captive_portal_service->set_test_url(test_url);
|
| + captive_portal_service->SetPortalDetectionEnabledForTest(true);
|
|
|
| // Don't use any non-zero timers. Timers are checked in unit tests.
|
| CaptivePortalService::RecheckPolicy* recheck_policy =
|
| @@ -1012,6 +1079,17 @@ bool CaptivePortalBrowserTest::CheckPending(Browser* browser) {
|
| captive_portal_service->TimerRunning();
|
| }
|
|
|
| +const void* CaptivePortalBrowserTest::GetInterstitialType(
|
| + WebContents* contents) const {
|
| + if (!contents->ShowingInterstitialPage())
|
| + return NULL;
|
| + SecurityInterstitialPage* blocking_page =
|
| + static_cast<SecurityInterstitialPage*>(
|
| + contents->GetInterstitialPage()->GetDelegateForTesting());
|
| + DCHECK(blocking_page);
|
| + return blocking_page->GetTypeForTesting();
|
| +}
|
| +
|
| CaptivePortalTabReloader::State CaptivePortalBrowserTest::GetStateOfTabReloader(
|
| WebContents* web_contents) const {
|
| return GetTabReloader(web_contents)->state();
|
| @@ -1244,13 +1322,15 @@ void CaptivePortalBrowserTest::FastTimeoutBehindCaptivePortal(
|
| bool expect_open_login_tab) {
|
| FastErrorBehindCaptivePortal(browser,
|
| expect_open_login_tab,
|
| - GURL(kMockHttpsQuickTimeoutUrl));
|
| + GURL(kMockHttpsQuickTimeoutUrl),
|
| + false);
|
| }
|
|
|
| void CaptivePortalBrowserTest::FastErrorBehindCaptivePortal(
|
| Browser* browser,
|
| bool expect_open_login_tab,
|
| - const GURL& error_url) {
|
| + const GURL& error_url,
|
| + bool disable_portal_check_until_interstitial) {
|
| 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.
|
| @@ -1274,12 +1354,27 @@ void CaptivePortalBrowserTest::FastErrorBehindCaptivePortal(
|
| ++expected_broken_tabs;
|
| }
|
|
|
| + CaptivePortalService* captive_portal_service =
|
| + CaptivePortalServiceFactory::GetForProfile(browser->profile());
|
| + if (disable_portal_check_until_interstitial)
|
| + captive_portal_service->SetPortalDetectionEnabledForTest(false);
|
| +
|
| MultiNavigationObserver navigation_observer;
|
| CaptivePortalObserver portal_observer(browser->profile());
|
| ui_test_utils::NavigateToURLWithDisposition(browser,
|
| error_url,
|
| CURRENT_TAB,
|
| ui_test_utils::BROWSER_TEST_NONE);
|
| +
|
| + if (disable_portal_check_until_interstitial) {
|
| + EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE,
|
| + GetStateOfTabReloaderAt(browser, initial_active_index));
|
| + // Once the interstitial is attached, probe for captive portal.
|
| + WaitForInterstitialAttach(tab_strip_model->GetActiveWebContents());
|
| + captive_portal_service->SetPortalDetectionEnabledForTest(true);
|
| + captive_portal_service->DetectCaptivePortal();
|
| + }
|
| +
|
| portal_observer.WaitForResults(1);
|
|
|
| if (expect_open_login_tab) {
|
| @@ -1407,6 +1502,43 @@ void CaptivePortalBrowserTest::Login(Browser* browser,
|
| tab_strip_model->GetWebContentsAt(login_tab_index)));
|
| }
|
|
|
| +void CaptivePortalBrowserTest::LoginCertError(Browser* browser) {
|
| + TabStripModel* tab_strip_model = browser->tab_strip_model();
|
| + URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortal(false);
|
| + MultiNavigationObserver navigation_observer;
|
| + CaptivePortalObserver portal_observer(browser->profile());
|
| +
|
| + content::RenderFrameHost* render_frame_host =
|
| + tab_strip_model->GetActiveWebContents()->GetMainFrame();
|
| + render_frame_host->ExecuteJavaScript(base::ASCIIToUTF16("submitForm()"));
|
| +
|
| + // The captive portal tab navigation will trigger a captive portal check,
|
| + // and reloading the original tab will bring up the interstitial page again,
|
| + // triggering a second captive portal check.
|
| + portal_observer.WaitForResults(2);
|
| +
|
| + // Wait for both tabs to finish loading.
|
| + navigation_observer.WaitForNavigations(2);
|
| + EXPECT_EQ(2, portal_observer.num_results_received());
|
| + EXPECT_FALSE(CheckPending(browser));
|
| + EXPECT_EQ(captive_portal::RESULT_INTERNET_CONNECTED,
|
| + portal_observer.captive_portal_result());
|
| +
|
| + // Check state of tabs. While the first tab is still displaying an
|
| + // interstitial page, since no portal was found, it should be in STATE_NONE,
|
| + // as should the login tab.
|
| + ASSERT_EQ(2, tab_strip_model->count());
|
| + EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE,
|
| + GetStateOfTabReloaderAt(browser, 0));
|
| + EXPECT_FALSE(IsLoginTab(tab_strip_model->GetWebContentsAt(1)));
|
| + EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE,
|
| + GetStateOfTabReloaderAt(browser, 1));
|
| +
|
| + // Make sure only one navigation was for the login tab.
|
| + EXPECT_EQ(1, navigation_observer.NumNavigationsForTab(
|
| + tab_strip_model->GetWebContentsAt(1)));
|
| +}
|
| +
|
| void CaptivePortalBrowserTest::FailLoadsAfterLogin(Browser* browser,
|
| int num_loading_tabs) {
|
| ASSERT_EQ(num_loading_tabs, NumLoadingTabs());
|
| @@ -1546,6 +1678,12 @@ void CaptivePortalBrowserTest::SetSlowSSLLoadTime(
|
| tab_reloader->set_slow_ssl_load_time(slow_ssl_load_time);
|
| }
|
|
|
| +void CaptivePortalBrowserTest::SetSSLErrorDisplayDelay(
|
| + CaptivePortalTabHelper* tab_helper,
|
| + base::TimeDelta ssl_error_delay) {
|
| + tab_helper->SetSSLErrorDelayForTest(ssl_error_delay);
|
| +}
|
| +
|
| CaptivePortalTabReloader* CaptivePortalBrowserTest::GetTabReloader(
|
| WebContents* web_contents) const {
|
| return CaptivePortalTabHelper::FromWebContents(web_contents)->
|
| @@ -1705,11 +1843,9 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginFastTimeout) {
|
| }
|
|
|
| // A cert error triggers a captive portal check and results in opening a login
|
| -// tab. The user then logs in and the page with the error is reloaded.
|
| -IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, SSLCertErrorLogin) {
|
| - // Need an HTTP TestServer to handle a dynamically created server redirect.
|
| - ASSERT_TRUE(test_server()->Start());
|
| -
|
| +// tab.
|
| +IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest,
|
| + ShowCaptivePortalInterstitialOnCertError) {
|
| net::SpawnedTestServer::SSLOptions https_options;
|
| https_options.server_certificate =
|
| net::SpawnedTestServer::SSLOptions::CERT_MISMATCHED_NAME;
|
| @@ -1718,49 +1854,334 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, SSLCertErrorLogin) {
|
| base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
|
| ASSERT_TRUE(https_server.Start());
|
|
|
| + // 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.
|
| + TabStripModel* tab_strip_model = browser()->tab_strip_model();
|
| + WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents();
|
| + SetSSLErrorDisplayDelay(
|
| + CaptivePortalTabHelper::FromWebContents(broken_tab_contents),
|
| + base::TimeDelta::FromHours(1));
|
| +
|
| // 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);
|
| + FastErrorBehindCaptivePortal(browser(), true, cert_error_url, false);
|
|
|
| - // Simulate logging in. Can't use Login() because the interstitial tab looks
|
| - // like a cross between a hung tab (Load was never committed) and a tab at an
|
| - // error page (The load was stopped).
|
| - URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortal(false);
|
| - MultiNavigationObserver navigation_observer;
|
| + EXPECT_EQ(CaptivePortalBlockingPage::kTypeForTesting,
|
| + GetInterstitialType(broken_tab_contents));
|
| +
|
| + int login_tab_index = tab_strip_model->active_index();
|
| + // Switch to the interstitial and click the "Connect" button. Should switch
|
| + // active tab to the captive portal landing page.
|
| + tab_strip_model->ActivateTabAt(cert_error_tab_index, false);
|
| +
|
| + content::RenderViewHost* rvh =
|
| + broken_tab_contents->GetInterstitialPage()->GetRenderViewHostForTesting();
|
| + EXPECT_TRUE(WaitForPageReady(rvh));
|
| + EXPECT_TRUE(
|
| + content::ExecuteScript(
|
| + rvh->GetMainFrame(),
|
| + "document.getElementById('primary-button').click();"));
|
| + EXPECT_EQ(login_tab_index, tab_strip_model->active_index());
|
| +
|
| + LoginCertError(browser());
|
| +
|
| + // Once logged in, broken tab should reload and display the SSL interstitial.
|
| + WaitForInterstitialAttach(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.
|
| + URLRequestMockCaptivePortalJobFactory::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));
|
| +}
|
| +
|
| +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.
|
| + CaptivePortalService* captive_portal_service =
|
| + CaptivePortalServiceFactory::GetForProfile(browser->profile());
|
| + captive_portal_service->SetPortalDetectionEnabledForTest(false);
|
| +
|
| + content::WindowedNotificationObserver signal(
|
| + chrome::NOTIFICATION_SSL_INTERSTITIAL_TIMER_FIRED,
|
| + content::Source<WebContents>(broken_tab_contents));
|
| + ui_test_utils::NavigateToURLWithDisposition(browser,
|
| + cert_error_url,
|
| + CURRENT_TAB,
|
| + ui_test_utils::BROWSER_TEST_NONE);
|
| + signal.Wait();
|
| + ASSERT_EQ(broken_tab_contents,
|
| + content::Source<WebContents>(signal.source()).ptr());
|
| +
|
| + // 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());
|
| +}
|
| +
|
| +// Tests this scenario:
|
| +// - Portal checks are disabled.
|
| +// - 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.
|
| +// - Stopping the page load shouldn't result in any interstitials.
|
| +IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest,
|
| + InterstitialTimerStopNavigationWhileLoading) {
|
| + net::SpawnedTestServer::SSLOptions https_options;
|
| + https_options.server_certificate =
|
| + net::SpawnedTestServer::SSLOptions::CERT_MISMATCHED_NAME;
|
| + net::SpawnedTestServer https_server(
|
| + net::SpawnedTestServer::TYPE_HTTPS, https_options,
|
| + base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
|
| + ASSERT_TRUE(https_server.Start());
|
| + // The path does not matter.
|
| + GURL cert_error_url = https_server.GetURL(kTestServerLoginPath);
|
|
|
| TabStripModel* tab_strip_model = browser()->tab_strip_model();
|
| - content::RenderFrameHost* render_frame_host =
|
| - tab_strip_model->GetActiveWebContents()->GetMainFrame();
|
| - render_frame_host->ExecuteJavaScript(base::ASCIIToUTF16("submitForm()"));
|
| + WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents();
|
| + SetSSLErrorDisplayDelay(
|
| + CaptivePortalTabHelper::FromWebContents(broken_tab_contents),
|
| + base::TimeDelta::FromHours(1));
|
| +
|
| + 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);
|
| +
|
| + EXPECT_FALSE(broken_tab_contents->ShowingInterstitialPage());
|
| + 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));
|
|
|
| - // 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);
|
| + // Re-enable captive portal checks and fire one. The result should be ignored.
|
| + CaptivePortalObserver portal_observer2(browser()->profile());
|
| + CaptivePortalService* captive_portal_service =
|
| + CaptivePortalServiceFactory::GetForProfile(browser()->profile());
|
| + captive_portal_service->SetPortalDetectionEnabledForTest(true);
|
| + captive_portal_service->DetectCaptivePortal();
|
| + portal_observer2.WaitForResults(1);
|
|
|
| - // Wait for both tabs to finish loading.
|
| - navigation_observer.WaitForNavigations(2);
|
| - EXPECT_EQ(2, portal_observer.num_results_received());
|
| + EXPECT_FALSE(broken_tab_contents->ShowingInterstitialPage());
|
| + 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(captive_portal::RESULT_INTERNET_CONNECTED,
|
| - portal_observer.captive_portal_result());
|
| + EXPECT_EQ(1, browser()->tab_strip_model()->count());
|
| + EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE,
|
| + GetStateOfTabReloaderAt(browser(), 0));
|
| +}
|
|
|
| - // 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());
|
| +// 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::SpawnedTestServer::SSLOptions https_options;
|
| + https_options.server_certificate =
|
| + net::SpawnedTestServer::SSLOptions::CERT_MISMATCHED_NAME;
|
| + net::SpawnedTestServer https_server(
|
| + net::SpawnedTestServer::TYPE_HTTPS, https_options,
|
| + base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
|
| + ASSERT_TRUE(https_server.Start());
|
| + // The path does not matter.
|
| + GURL cert_error_url = https_server.GetURL(kTestServerLoginPath);
|
| +
|
| + // 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.
|
| + TabStripModel* tab_strip_model = browser()->tab_strip_model();
|
| + WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents();
|
| + SetSSLErrorDisplayDelay(
|
| + CaptivePortalTabHelper::FromWebContents(broken_tab_contents),
|
| + base::TimeDelta::FromHours(1));
|
| +
|
| + CaptivePortalObserver portal_observer(browser()->profile());
|
| + FastErrorWithInterstitialTimer(browser(), cert_error_url);
|
| +
|
| + // Page appears loading. Reloading should cancel the navigation and there
|
| + // should be no interstitial.
|
| + MultiNavigationObserver test_navigation_observer;
|
| + chrome::Reload(browser(), CURRENT_TAB);
|
| + test_navigation_observer.WaitForNavigations(2);
|
| +
|
| + EXPECT_FALSE(broken_tab_contents->ShowingInterstitialPage());
|
| + EXPECT_FALSE(broken_tab_contents->IsLoading());
|
| + EXPECT_EQ(0, portal_observer.num_results_received());
|
| + EXPECT_EQ(2, test_navigation_observer.num_navigations());
|
| + EXPECT_EQ(0, NumLoadingTabs());
|
| + EXPECT_FALSE(CheckPending(browser()));
|
| + EXPECT_EQ(1, browser()->tab_strip_model()->count());
|
| EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE,
|
| GetStateOfTabReloaderAt(browser(), 0));
|
| - EXPECT_FALSE(IsLoginTab(tab_strip_model->GetWebContentsAt(1)));
|
| +
|
| + // Re-enable captive portal checks and fire one. The result should be ignored.
|
| + CaptivePortalObserver portal_observer2(browser()->profile());
|
| + CaptivePortalService* captive_portal_service =
|
| + CaptivePortalServiceFactory::GetForProfile(browser()->profile());
|
| + captive_portal_service->SetPortalDetectionEnabledForTest(true);
|
| + captive_portal_service->DetectCaptivePortal();
|
| + portal_observer2.WaitForResults(1);
|
| +
|
| + EXPECT_FALSE(broken_tab_contents->ShowingInterstitialPage());
|
| + 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(), 1));
|
| + GetStateOfTabReloaderAt(browser(), 0));
|
| +}
|
|
|
| - // Make sure only one navigation was for the login tab.
|
| - EXPECT_EQ(1, navigation_observer.NumNavigationsForTab(
|
| - tab_strip_model->GetWebContentsAt(1)));
|
| +// Same as above, but instead of reloading, the page is navigated away. The new
|
| +// page should load, and no interstitials shoudl be shown.
|
| +IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest,
|
| + InterstitialTimerNavigateAwayWhileLoading) {
|
| + net::SpawnedTestServer::SSLOptions https_options;
|
| + https_options.server_certificate =
|
| + net::SpawnedTestServer::SSLOptions::CERT_MISMATCHED_NAME;
|
| + net::SpawnedTestServer https_server(
|
| + net::SpawnedTestServer::TYPE_HTTPS, https_options,
|
| + base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
|
| + ASSERT_TRUE(https_server.Start());
|
| + // The path does not matter.
|
| + GURL cert_error_url = https_server.GetURL(kTestServerLoginPath);
|
| +
|
| + // 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.
|
| + TabStripModel* tab_strip_model = browser()->tab_strip_model();
|
| + WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents();
|
| + SetSSLErrorDisplayDelay(
|
| + CaptivePortalTabHelper::FromWebContents(broken_tab_contents),
|
| + base::TimeDelta::FromHours(1));
|
| +
|
| + 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(
|
| + URLRequestMockHTTPJob::GetMockUrl(
|
| + base::FilePath(FILE_PATH_LITERAL("title2.html"))),
|
| + content::Referrer(),
|
| + CURRENT_TAB,
|
| + ui::PAGE_TRANSITION_TYPED, false));
|
| + test_navigation_observer.WaitForNavigations(2);
|
| +
|
| + EXPECT_FALSE(broken_tab_contents->ShowingInterstitialPage());
|
| + EXPECT_FALSE(broken_tab_contents->IsLoading());
|
| + EXPECT_EQ(0, portal_observer.num_results_received());
|
| + EXPECT_EQ(2, test_navigation_observer.num_navigations());
|
| + 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.
|
| + CaptivePortalObserver portal_observer2(browser()->profile());
|
| + CaptivePortalService* captive_portal_service =
|
| + CaptivePortalServiceFactory::GetForProfile(browser()->profile());
|
| + captive_portal_service->SetPortalDetectionEnabledForTest(true);
|
| + captive_portal_service->DetectCaptivePortal();
|
| + portal_observer2.WaitForResults(1);
|
| +
|
| + EXPECT_FALSE(broken_tab_contents->ShowingInterstitialPage());
|
| + 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));
|
| +}
|
| +
|
| +// A cert error triggers a captive portal check and results in opening a login
|
| +// tab. The user then logs in and the page with the error is reloaded.
|
| +IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, SSLCertErrorLogin) {
|
| + // Need an HTTP TestServer to handle a dynamically created server redirect.
|
| + ASSERT_TRUE(test_server()->Start());
|
| +
|
| + net::SpawnedTestServer::SSLOptions https_options;
|
| + https_options.server_certificate =
|
| + net::SpawnedTestServer::SSLOptions::CERT_MISMATCHED_NAME;
|
| + net::SpawnedTestServer https_server(
|
| + net::SpawnedTestServer::TYPE_HTTPS, https_options,
|
| + base::FilePath(FILE_PATH_LITERAL("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.
|
| + TabStripModel* tab_strip_model = browser()->tab_strip_model();
|
| + WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents();
|
| + SetSSLErrorDisplayDelay(
|
| + CaptivePortalTabHelper::FromWebContents(broken_tab_contents),
|
| + base::TimeDelta());
|
| +
|
| + // Setting the delay to zero above has a race condition: A captive portal
|
| + // result triggered by a cert error can arrive before the SSL interstitial
|
| + // display timer is fired, even though it's set to zero.
|
| + // To avoid this, disable captive portal checks until the SSL interstitial is
|
| + // displayed. Once it's displayed, enable portal checks and fire one.
|
| + bool disable_portal_check_until_interstitial = true;
|
| +
|
| + // The path does not matter.
|
| + GURL cert_error_url = https_server.GetURL(kTestServerLoginPath);
|
| + // The interstitial should trigger a captive portal check when it opens, just
|
| + // like navigating to kMockHttpsQuickTimeoutUrl.
|
| + FastErrorBehindCaptivePortal(
|
| + browser(),
|
| + true,
|
| + cert_error_url,
|
| + disable_portal_check_until_interstitial);
|
| +
|
| + EXPECT_EQ(SSLBlockingPage::kTypeForTesting,
|
| + GetInterstitialType(broken_tab_contents));
|
| +
|
| + LoginCertError(browser());
|
| }
|
|
|
| // Tries navigating both the tab that encounters an SSL timeout and the
|
| @@ -2213,3 +2634,42 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, HstsLogin) {
|
| 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) {
|
| + // Use a url that triggers a slow load, instead of creating an https server.
|
| + GURL cert_error_url = GURL(kMockHttpsUrl);
|
| +
|
| + 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();
|
| + CaptivePortalTabReloader* tab_reloader = GetTabReloader(broken_tab_contents);
|
| + SetSlowSSLLoadTime(tab_reloader, base::TimeDelta());
|
| +
|
| + 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.
|
| + URLRequestMockCaptivePortalJobFactory::SetBehindCaptivePortal(false);
|
| +
|
| + CaptivePortalObserver portal_observer(browser()->profile());
|
| + MultiNavigationObserver navigation_observer;
|
| + URLRequestTimeoutOnDemandJob::FailJobsWithCertError(1);
|
| + navigation_observer.WaitForNavigations(1);
|
| +
|
| + EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD,
|
| + GetStateOfTabReloaderAt(browser(), broken_tab_index));
|
| +
|
| + WaitForInterstitialAttach(broken_tab_contents);
|
| + portal_observer.WaitForResults(1);
|
| +
|
| + EXPECT_EQ(SSLBlockingPage::kTypeForTesting,
|
| + GetInterstitialType(broken_tab_contents));
|
| +}
|
|
|