Commit 7fdea651 authored by dalyk's avatar dalyk Committed by Commit Bot

Trigger captive portal checks on secure DNS network failures.

These captive portal checks may occur on either HTTP or HTTPS
navigations. The CaptivePortalTabHelper no longer tracks navigations
that are renderer-initiated to avoid interference from link doctor and
renderer-initiated reload attempts (these navigations, which can be
triggered by DNS failures, may otherwise reset the tab state before a
captive portal probe result is received).

Bug: 10161646
Change-Id: Ia2ac3715d43e9b1be71df1dea7a1882f98033888
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1870013Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Commit-Queue: Katharine Daly <dalyk@google.com>
Cr-Commit-Position: refs/heads/master@{#737843}
parent f5d536ed
...@@ -159,6 +159,10 @@ const char* const kMockHttpsConnectionUnexpectedErr = ...@@ -159,6 +159,10 @@ const char* const kMockHttpsConnectionUnexpectedErr =
"https://mock.captive.portal.quick.error/unexpected"; "https://mock.captive.portal.quick.error/unexpected";
const char* const kMockHttpConnectionConnectionClosedErr = const char* const kMockHttpConnectionConnectionClosedErr =
"http://mock.captive.portal.quick.error/connection_closed"; "http://mock.captive.portal.quick.error/connection_closed";
const char* const kMockHttpConnectionSecureDnsErr =
"http://mock.captive.portal.quick.error/secure_dns";
const char* const kMockHttpsConnectionSecureDnsErr =
"https://mock.captive.portal.quick.error/secure_dns";
// Expected title of a tab once an HTTPS load completes, when not behind a // Expected title of a tab once an HTTPS load completes, when not behind a
// captive portal. // captive portal.
...@@ -684,6 +688,7 @@ class CaptivePortalBrowserTest : public InProcessBrowserTest { ...@@ -684,6 +688,7 @@ class CaptivePortalBrowserTest : public InProcessBrowserTest {
// result in an error rather than hanging. // result in an error rather than hanging.
void FastErrorBehindCaptivePortal(Browser* browser, void FastErrorBehindCaptivePortal(Browser* browser,
bool expect_open_login_tab, bool expect_open_login_tab,
bool expect_new_login_browser,
const GURL& error_url); const GURL& error_url);
// Navigates the active tab to an SSL error page which triggers an // Navigates the active tab to an SSL error page which triggers an
...@@ -706,9 +711,11 @@ class CaptivePortalBrowserTest : public InProcessBrowserTest { ...@@ -706,9 +711,11 @@ class CaptivePortalBrowserTest : public InProcessBrowserTest {
// |captive-portal_browser| is the browser containing the login page. // |captive-portal_browser| is the browser containing the login page.
// |num_loading_tabs| and |num_timed_out_tabs| are used as extra checks // |num_loading_tabs| and |num_timed_out_tabs| are used as extra checks
// that nothing has gone wrong prior to the function call. // that nothing has gone wrong prior to the function call.
// |expected_portal_checks| allows client-side redirects to be tested.
void Login(Browser* captive_portal_browser, void Login(Browser* captive_portal_browser,
int num_loading_tabs, int num_loading_tabs,
int num_timed_out_tabs); int num_timed_out_tabs,
int expected_portal_checks);
// Simulates a login when the broken tab shows an SSL or captive portal // Simulates a login when the broken tab shows an SSL or captive portal
// interstitial. Can't use Login() in those cases because the interstitial // interstitial. Can't use Login() in those cases because the interstitial
...@@ -791,22 +798,27 @@ class CaptivePortalBrowserTest : public InProcessBrowserTest { ...@@ -791,22 +798,27 @@ class CaptivePortalBrowserTest : public InProcessBrowserTest {
num_jobs_to_wait_for_ = num_jobs; num_jobs_to_wait_for_ = num_jobs;
} }
// Fails all active kMockHttps* requests with connection timeouts. // Fails all active kMockHttps* requests with error code |error| and
// hostname resolution error info |resolve_error_info|.
// There are expected to be exactly |expected_num_jobs| waiting for // There are expected to be exactly |expected_num_jobs| waiting for
// failure. The only way to guarantee 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. // WaitForJobs, so makes sure there has been a matching WaitForJobs call.
void FailJobs(int expected_num_jobs) { void FailJobs(int expected_num_jobs,
int error,
net::ResolveErrorInfo resolve_error_info) {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
base::PostTask(FROM_HERE, {BrowserThread::UI}, base::PostTask(FROM_HERE, {BrowserThread::UI},
base::BindOnce(&CaptivePortalBrowserTest::FailJobs, base::BindOnce(&CaptivePortalBrowserTest::FailJobs,
base::Unretained(this), expected_num_jobs)); base::Unretained(this), expected_num_jobs,
error, resolve_error_info));
return; return;
} }
EXPECT_EQ(expected_num_jobs, EXPECT_EQ(expected_num_jobs,
static_cast<int>(ongoing_mock_requests_.size())); static_cast<int>(ongoing_mock_requests_.size()));
network::URLLoaderCompletionStatus status; network::URLLoaderCompletionStatus status;
status.error_code = net::ERR_CONNECTION_TIMED_OUT; status.error_code = error;
status.resolve_error_info = resolve_error_info;
for (auto& job : ongoing_mock_requests_) for (auto& job : ongoing_mock_requests_)
job.client->OnComplete(status); job.client->OnComplete(status);
ongoing_mock_requests_.clear(); ongoing_mock_requests_.clear();
...@@ -957,17 +969,23 @@ bool CaptivePortalBrowserTest::OnIntercept( ...@@ -957,17 +969,23 @@ bool CaptivePortalBrowserTest::OnIntercept(
} }
auto url_string = params->url_request.url.spec(); auto url_string = params->url_request.url.spec();
net::Error error = net::OK; network::URLLoaderCompletionStatus status;
status.error_code = net::OK;
if (url_string == kMockHttpConnectionTimeoutErr || if (url_string == kMockHttpConnectionTimeoutErr ||
url_string == kMockHttpsConnectionTimeoutErr) { url_string == kMockHttpsConnectionTimeoutErr) {
error = net::ERR_CONNECTION_TIMED_OUT; status.error_code = net::ERR_CONNECTION_TIMED_OUT;
} else if (url_string == kMockHttpsConnectionUnexpectedErr) { } else if (url_string == kMockHttpsConnectionUnexpectedErr) {
error = net::ERR_UNEXPECTED; status.error_code = net::ERR_UNEXPECTED;
} else if (url_string == kMockHttpConnectionConnectionClosedErr) { } else if (url_string == kMockHttpConnectionConnectionClosedErr) {
error = net::ERR_CONNECTION_CLOSED; status.error_code = net::ERR_CONNECTION_CLOSED;
} else if (url_string == kMockHttpConnectionSecureDnsErr ||
url_string == kMockHttpsConnectionSecureDnsErr) {
status.error_code = net::ERR_NAME_NOT_RESOLVED;
status.resolve_error_info = net::ResolveErrorInfo(
net::ERR_CERT_COMMON_NAME_INVALID, true /* is_secure_network_error */);
} }
if (error != net::OK) { if (status.error_code != net::OK) {
params->client->OnComplete(network::URLLoaderCompletionStatus(error)); params->client->OnComplete(status);
return true; return true;
} }
...@@ -1204,7 +1222,7 @@ void CaptivePortalBrowserTest::SlowLoadNoCaptivePortal( ...@@ -1204,7 +1222,7 @@ void CaptivePortalBrowserTest::SlowLoadNoCaptivePortal(
// Wait for the request to be issued, then time it out. // Wait for the request to be issued, then time it out.
WaitForJobs(1); WaitForJobs(1);
FailJobs(1); FailJobs(1, net::ERR_CONNECTION_TIMED_OUT, net::ResolveErrorInfo(net::OK));
navigation_observer.WaitForNavigations(1); navigation_observer.WaitForNavigations(1);
ASSERT_EQ(1, browser->tab_strip_model()->count()); ASSERT_EQ(1, browser->tab_strip_model()->count());
...@@ -1374,12 +1392,14 @@ void CaptivePortalBrowserTest::FastTimeoutBehindCaptivePortal( ...@@ -1374,12 +1392,14 @@ void CaptivePortalBrowserTest::FastTimeoutBehindCaptivePortal(
Browser* browser, Browser* browser,
bool expect_open_login_tab) { bool expect_open_login_tab) {
FastErrorBehindCaptivePortal(browser, expect_open_login_tab, FastErrorBehindCaptivePortal(browser, expect_open_login_tab,
false /* expect_new_login_browser */,
GURL(kMockHttpsQuickTimeoutUrl)); GURL(kMockHttpsQuickTimeoutUrl));
} }
void CaptivePortalBrowserTest::FastErrorBehindCaptivePortal( void CaptivePortalBrowserTest::FastErrorBehindCaptivePortal(
Browser* browser, Browser* browser,
bool expect_open_login_tab, bool expect_open_login_tab,
bool expect_new_login_browser,
const GURL& error_url) { const GURL& error_url) {
TabStripModel* tab_strip_model = browser->tab_strip_model(); TabStripModel* tab_strip_model = browser->tab_strip_model();
// Calling this on a tab that's waiting for a load to manually be timed out // Calling this on a tab that's waiting for a load to manually be timed out
...@@ -1399,6 +1419,7 @@ void CaptivePortalBrowserTest::FastErrorBehindCaptivePortal( ...@@ -1399,6 +1419,7 @@ void CaptivePortalBrowserTest::FastErrorBehindCaptivePortal(
int initial_active_index = tab_strip_model->active_index(); int initial_active_index = tab_strip_model->active_index();
int initial_loading_tabs = NumLoadingTabs(); int initial_loading_tabs = NumLoadingTabs();
int expected_broken_tabs = NumBrokenTabs(); int expected_broken_tabs = NumBrokenTabs();
size_t initial_browser_count = browser_list_->size();
if (CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL != if (CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL !=
GetStateOfTabReloader(tab_strip_model->GetActiveWebContents())) { GetStateOfTabReloader(tab_strip_model->GetActiveWebContents())) {
++expected_broken_tabs; ++expected_broken_tabs;
...@@ -1414,19 +1435,40 @@ void CaptivePortalBrowserTest::FastErrorBehindCaptivePortal( ...@@ -1414,19 +1435,40 @@ void CaptivePortalBrowserTest::FastErrorBehindCaptivePortal(
if (expect_open_login_tab) { if (expect_open_login_tab) {
navigation_observer.WaitForNavigations(2); navigation_observer.WaitForNavigations(2);
ASSERT_EQ(initial_tab_count + 1, tab_strip_model->count()); WebContents* login_tab;
EXPECT_EQ(initial_tab_count, tab_strip_model->active_index());
// Make sure that the originally active tab and the captive portal tab have if (expect_new_login_browser) {
// each loaded once. ASSERT_EQ(initial_browser_count + 1, browser_list_->size());
// Check the original browser
ASSERT_EQ(initial_tab_count, tab_strip_model->count());
EXPECT_EQ(initial_tab_count - 1, tab_strip_model->active_index());
EXPECT_NE(browser_list_->get(initial_browser_count - 1),
browser_list_->GetLastActive());
// Check the new popup browser
Browser* popup_browser = browser_list_->get(initial_browser_count);
EXPECT_EQ(popup_browser, browser_list_->GetLastActive());
EXPECT_EQ(Browser::TYPE_POPUP, popup_browser->type());
login_tab = popup_browser->tab_strip_model()->GetWebContentsAt(0);
EXPECT_TRUE(CaptivePortalTabHelper::FromWebContents(login_tab)
->is_captive_portal_window());
} else {
ASSERT_EQ(initial_browser_count, browser_list_->size());
ASSERT_EQ(initial_tab_count + 1, tab_strip_model->count());
EXPECT_EQ(initial_tab_count, tab_strip_model->active_index());
login_tab = tab_strip_model->GetWebContentsAt(initial_tab_count);
}
EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( EXPECT_EQ(1, navigation_observer.NumNavigationsForTab(
tab_strip_model->GetWebContentsAt(initial_active_index))); tab_strip_model->GetWebContentsAt(initial_active_index)));
EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( EXPECT_EQ(1, navigation_observer.NumNavigationsForTab(login_tab));
tab_strip_model->GetWebContentsAt(initial_tab_count)));
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE,
GetStateOfTabReloaderAt(browser, 1)); GetStateOfTabReloader(login_tab));
EXPECT_TRUE(IsLoginTab(tab_strip_model->GetWebContentsAt(1))); EXPECT_TRUE(IsLoginTab(login_tab));
} else { } else {
navigation_observer.WaitForNavigations(1); navigation_observer.WaitForNavigations(1);
ASSERT_EQ(initial_browser_count, browser_list_->size());
EXPECT_EQ(initial_active_index, tab_strip_model->active_index()); EXPECT_EQ(initial_active_index, tab_strip_model->active_index());
EXPECT_EQ(1, navigation_observer.NumNavigationsForTab( EXPECT_EQ(1, navigation_observer.NumNavigationsForTab(
tab_strip_model->GetWebContentsAt(initial_active_index))); tab_strip_model->GetWebContentsAt(initial_active_index)));
...@@ -1512,7 +1554,8 @@ void CaptivePortalBrowserTest::NavigateLoginTab(Browser* browser, ...@@ -1512,7 +1554,8 @@ void CaptivePortalBrowserTest::NavigateLoginTab(Browser* browser,
void CaptivePortalBrowserTest::Login(Browser* captive_portal_browser, void CaptivePortalBrowserTest::Login(Browser* captive_portal_browser,
int num_loading_tabs, int num_loading_tabs,
int num_timed_out_tabs) { int num_timed_out_tabs,
int expected_portal_checks) {
// Simulate logging in. // Simulate logging in.
SetBehindCaptivePortal(false); SetBehindCaptivePortal(false);
...@@ -1536,9 +1579,11 @@ void CaptivePortalBrowserTest::Login(Browser* captive_portal_browser, ...@@ -1536,9 +1579,11 @@ void CaptivePortalBrowserTest::Login(Browser* captive_portal_browser,
"submitForm()"); "submitForm()");
portal_observer.WaitForResults(1); portal_observer.WaitForResults(1);
// Wait for all the timed out tabs to reload. // Wait for all the timed out tabs to reload and any new portal checks
// triggered by the reloads.
navigation_observer.WaitForNavigations(1 + num_timed_out_tabs); navigation_observer.WaitForNavigations(1 + num_timed_out_tabs);
EXPECT_EQ(1, portal_observer.num_results_received()); portal_observer.WaitForResults(expected_portal_checks);
EXPECT_EQ(expected_portal_checks, portal_observer.num_results_received());
// The tabs that were loading before should still be loading, and now be in // The tabs that were loading before should still be loading, and now be in
// STATE_NEEDS_RELOAD. // STATE_NEEDS_RELOAD.
...@@ -1619,7 +1664,8 @@ void CaptivePortalBrowserTest::FailLoadsAfterLogin(Browser* browser, ...@@ -1619,7 +1664,8 @@ void CaptivePortalBrowserTest::FailLoadsAfterLogin(Browser* browser,
// Connection(s) finally time out. There should have already been a call // Connection(s) finally time out. There should have already been a call
// to wait for the requests to be issued before logging on. // to wait for the requests to be issued before logging on.
WaitForJobs(num_loading_tabs); WaitForJobs(num_loading_tabs);
FailJobs(num_loading_tabs); FailJobs(num_loading_tabs, net::ERR_CONNECTION_TIMED_OUT,
net::ResolveErrorInfo(net::OK));
fail_loads_observer.WaitForNavigations(); fail_loads_observer.WaitForNavigations();
...@@ -1653,7 +1699,8 @@ void CaptivePortalBrowserTest::FailLoadsWithoutLogin(Browser* browser, ...@@ -1653,7 +1699,8 @@ void CaptivePortalBrowserTest::FailLoadsWithoutLogin(Browser* browser,
MultiNavigationObserver navigation_observer; MultiNavigationObserver navigation_observer;
// Connection(s) finally time out. There should have already been a call // Connection(s) finally time out. There should have already been a call
// to wait for the requests to be issued. // to wait for the requests to be issued.
FailJobs(num_loading_tabs); FailJobs(num_loading_tabs, net::ERR_CONNECTION_TIMED_OUT,
net::ResolveErrorInfo(net::OK));
navigation_observer.WaitForNavigations(num_loading_tabs); navigation_observer.WaitForNavigations(num_loading_tabs);
...@@ -1732,7 +1779,8 @@ void CaptivePortalBrowserTest::RunNavigateLoadingTabToTimeoutTest( ...@@ -1732,7 +1779,8 @@ void CaptivePortalBrowserTest::RunNavigateLoadingTabToTimeoutTest(
// Simulate logging in. // Simulate logging in.
tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther});
SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromDays(1)); SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromDays(1));
Login(browser, 1, 0); Login(browser, 1 /* num_loading_tabs */, 0 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
// Timeout occurs, and page is automatically reloaded. // Timeout occurs, and page is automatically reloaded.
FailLoadsAfterLogin(browser, 1); FailLoadsAfterLogin(browser, 1);
...@@ -1839,8 +1887,9 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, Login) { ...@@ -1839,8 +1887,9 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, Login) {
// Load starts, detect captive portal and open up a login tab. // Load starts, detect captive portal and open up a login tab.
SlowLoadBehindCaptivePortal(browser(), true); SlowLoadBehindCaptivePortal(browser(), true);
// Log in. One loading tab, no timed out ones. // Log in.
Login(browser(), 1, 0); Login(browser(), 1 /* num_loading_tabs */, 0 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
// Timeout occurs, and page is automatically reloaded. // Timeout occurs, and page is automatically reloaded.
FailLoadsAfterLogin(browser(), 1); FailLoadsAfterLogin(browser(), 1);
...@@ -1868,7 +1917,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginIncognito) { ...@@ -1868,7 +1917,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginIncognito) {
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE,
GetStateOfTabReloaderAt(browser(), 0)); GetStateOfTabReloaderAt(browser(), 0));
Login(incognito_browser, 1, 0); Login(incognito_browser, 1 /* num_loading_tabs */, 0 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
FailLoadsAfterLogin(incognito_browser, 1); FailLoadsAfterLogin(incognito_browser, 1);
EXPECT_EQ(1, tab_strip_model->count()); EXPECT_EQ(1, tab_strip_model->count());
...@@ -1885,14 +1935,16 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginIncognito) { ...@@ -1885,14 +1935,16 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginIncognito) {
IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginSlow) { IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginSlow) {
SlowLoadBehindCaptivePortal(browser(), true); SlowLoadBehindCaptivePortal(browser(), true);
FailLoadsWithoutLogin(browser(), 1); FailLoadsWithoutLogin(browser(), 1);
Login(browser(), 0, 1); Login(browser(), 0 /* num_loading_tabs */, 1 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
} }
// Checks the unlikely case that the tab times out before the timer triggers. // Checks the unlikely case that the tab times out before the timer triggers.
// This most likely won't happen, but should still work: // This most likely won't happen, but should still work:
IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginFastTimeout) { IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginFastTimeout) {
FastTimeoutBehindCaptivePortal(browser(), true); FastTimeoutBehindCaptivePortal(browser(), true);
Login(browser(), 0, 1); Login(browser(), 0 /* num_loading_tabs */, 1 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
} }
// Test that a navigation in a tab that is part of a captive portal windoow // Test that a navigation in a tab that is part of a captive portal windoow
...@@ -1951,7 +2003,9 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, ...@@ -1951,7 +2003,9 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest,
int cert_error_tab_index = tab_strip_model->active_index(); int cert_error_tab_index = tab_strip_model->active_index();
// The interstitial should trigger a captive portal check when it opens, just // The interstitial should trigger a captive portal check when it opens, just
// like navigating to kMockHttpsQuickTimeoutUrl. // like navigating to kMockHttpsQuickTimeoutUrl.
FastErrorBehindCaptivePortal(browser(), true, cert_error_url); FastErrorBehindCaptivePortal(browser(), true /* expect_open_login_tab */,
false /* expect_new_login_browser */,
cert_error_url);
EXPECT_EQ(CaptivePortalBlockingPage::kTypeForTesting, EXPECT_EQ(CaptivePortalBlockingPage::kTypeForTesting,
GetInterstitialType(broken_tab_contents)); GetInterstitialType(broken_tab_contents));
...@@ -2311,7 +2365,9 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, SSLCertErrorLogin) { ...@@ -2311,7 +2365,9 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, SSLCertErrorLogin) {
// The path does not matter. // The path does not matter.
GURL cert_error_url = https_server.GetURL(kTestServerLoginPath); GURL cert_error_url = https_server.GetURL(kTestServerLoginPath);
// A captive portal check is triggered in FastErrorBehindCaptivePortal. // A captive portal check is triggered in FastErrorBehindCaptivePortal.
FastErrorBehindCaptivePortal(browser(), true, cert_error_url); FastErrorBehindCaptivePortal(browser(), true /* expect_open_login_tab */,
false /* expect_new_login_browser */,
cert_error_url);
EXPECT_EQ(SSLBlockingPage::kTypeForTesting, EXPECT_EQ(SSLBlockingPage::kTypeForTesting,
GetInterstitialType(broken_tab_contents)); GetInterstitialType(broken_tab_contents));
...@@ -2335,7 +2391,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginExtraNavigations) { ...@@ -2335,7 +2391,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, LoginExtraNavigations) {
NavigateLoginTab(browser(), 0, 1); NavigateLoginTab(browser(), 0, 1);
// Simulate logging in. // Simulate logging in.
Login(browser(), 0, 1); Login(browser(), 0 /* num_loading_tabs */, 1 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
} }
// After the first SSL timeout, closes the login tab and makes sure it's opened // After the first SSL timeout, closes the login tab and makes sure it's opened
...@@ -2350,7 +2407,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, CloseLoginTab) { ...@@ -2350,7 +2407,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, CloseLoginTab) {
// Go through the standard slow load login, and make sure it still works. // Go through the standard slow load login, and make sure it still works.
SlowLoadBehindCaptivePortal(browser(), true); SlowLoadBehindCaptivePortal(browser(), true);
Login(browser(), 1, 0); Login(browser(), 1 /* num_loading_tabs */, 0 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
FailLoadsAfterLogin(browser(), 1); FailLoadsAfterLogin(browser(), 1);
} }
...@@ -2390,7 +2448,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, TwoBrokenTabs) { ...@@ -2390,7 +2448,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, TwoBrokenTabs) {
SlowLoadBehindCaptivePortal(browser(), false); SlowLoadBehindCaptivePortal(browser(), false);
tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther});
Login(browser(), 2, 0); Login(browser(), 2 /* num_loading_tabs */, 0 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
FailLoadsAfterLogin(browser(), 2); FailLoadsAfterLogin(browser(), 2);
} }
...@@ -2417,7 +2476,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, AbortLoad) { ...@@ -2417,7 +2476,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, AbortLoad) {
GetStateOfTabReloaderAt(browser(), 0)); GetStateOfTabReloaderAt(browser(), 0));
tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther});
Login(browser(), 0, 0); Login(browser(), 0 /* num_loading_tabs */, 0 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
} }
// Checks the case where the timed out tab is successfully navigated before // Checks the case where the timed out tab is successfully navigated before
...@@ -2438,7 +2498,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, NavigateBrokenTab) { ...@@ -2438,7 +2498,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, NavigateBrokenTab) {
// Simulate logging in. // Simulate logging in.
tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther}); tab_strip_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther});
Login(browser(), 0, 0); Login(browser(), 0 /* num_loading_tabs */, 0 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
} }
// Checks that captive portal detection triggers correctly when a same-site // Checks that captive portal detection triggers correctly when a same-site
...@@ -2562,7 +2623,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, GoBackToTimeout) { ...@@ -2562,7 +2623,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, GoBackToTimeout) {
EXPECT_EQ(1, NumLoadingTabs()); EXPECT_EQ(1, NumLoadingTabs());
SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromDays(1)); SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromDays(1));
Login(browser(), 1, 0); Login(browser(), 1 /* num_loading_tabs */, 0 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
FailLoadsAfterLogin(browser(), 1); FailLoadsAfterLogin(browser(), 1);
} }
...@@ -2616,7 +2678,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, ReloadTimeout) { ...@@ -2616,7 +2678,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, ReloadTimeout) {
EXPECT_EQ(1, NumLoadingTabs()); EXPECT_EQ(1, NumLoadingTabs());
SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromDays(1)); SetSlowSSLLoadTime(tab_reloader, base::TimeDelta::FromDays(1));
Login(browser(), 1, 0); Login(browser(), 1 /* num_loading_tabs */, 0 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
FailLoadsAfterLogin(browser(), 1); FailLoadsAfterLogin(browser(), 1);
} }
...@@ -2696,7 +2759,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, DISABLED_TwoWindows) { ...@@ -2696,7 +2759,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, DISABLED_TwoWindows) {
IsLoginTab(active_browser->tab_strip_model()->GetWebContentsAt(1))); IsLoginTab(active_browser->tab_strip_model()->GetWebContentsAt(1)));
// Simulate logging in. // Simulate logging in.
Login(active_browser, 0, 1); Login(active_browser, 0 /* num_loading_tabs */, 1 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
} }
// An HTTP page redirects to an HTTPS page loads slowly before timing out. A // An HTTP page redirects to an HTTPS page loads slowly before timing out. A
...@@ -2708,7 +2772,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, HttpToHttpsRedirectLogin) { ...@@ -2708,7 +2772,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, HttpToHttpsRedirectLogin) {
browser(), true /* expect_open_login_tab */, browser(), true /* expect_open_login_tab */,
false /* expect_new_login_browser */, false /* expect_new_login_browser */,
embedded_test_server()->GetURL(kRedirectToMockHttpsPath), 1, 1); embedded_test_server()->GetURL(kRedirectToMockHttpsPath), 1, 1);
Login(browser(), 1, 0); Login(browser(), 1 /* num_loading_tabs */, 0 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
FailLoadsAfterLogin(browser(), 1); FailLoadsAfterLogin(browser(), 1);
} }
...@@ -2737,7 +2802,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, Status511) { ...@@ -2737,7 +2802,8 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, Status511) {
SlowLoadBehindCaptivePortal(browser(), true /* expect_open_login_tab */, SlowLoadBehindCaptivePortal(browser(), true /* expect_open_login_tab */,
false /* expect_new_login_browser */, false /* expect_new_login_browser */,
GURL(kMockHttpsUrl), 2, 2); GURL(kMockHttpsUrl), 2, 2);
Login(browser(), 1, 0); Login(browser(), 1 /* num_loading_tabs */, 0 /* num_timed_out_tabs */,
1 /* expected_portal_checks */);
FailLoadsAfterLogin(browser(), 1); FailLoadsAfterLogin(browser(), 1);
} }
...@@ -2821,5 +2887,105 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, SecureDnsCaptivePortal) { ...@@ -2821,5 +2887,105 @@ IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, SecureDnsCaptivePortal) {
EXPECT_TRUE(browser_list_->get(1)->window()->IsVisible()); EXPECT_TRUE(browser_list_->get(1)->window()->IsVisible());
// Login to the captive portal. // Login to the captive portal.
Login(browser_list_->get(1), 2, 0); Login(browser_list_->get(1), 2 /* num_loading_tabs */,
0 /* num_timed_out_tabs */, 1 /* expected_portal_checks */);
}
// An HTTP load results in a secure DNS error, which triggers a captive portal
// probe that fails. After logging in, the secure DNS error happens again,
// triggering a captive portal probe that now succeeds.
IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest, SecureDnsErrorTriggersCheck) {
PrefService* local_state = g_browser_process->local_state();
local_state->SetString(prefs::kDnsOverHttpsTemplates,
"https://bar.test/dns-query{?dns}");
local_state->SetString(prefs::kDnsOverHttpsMode,
chrome_browser_net::kDnsOverHttpsModeSecure);
TabStripModel* tab_strip_model = browser()->tab_strip_model();
WebContents* broken_tab_contents = tab_strip_model->GetActiveWebContents();
FastErrorBehindCaptivePortal(browser(), true /* expect_open_login_tab */,
true /* expect_new_login_browser */,
GURL(kMockHttpConnectionSecureDnsErr));
// The navigated tab should be displaying an error page.
EXPECT_TRUE(broken_tab_contents->GetController()
.GetLastCommittedEntry()
->GetPageType() == content::PAGE_TYPE_ERROR);
// Login to the captive portal. The captive portal tab navigation will trigger
// a captive portal check, and reloading the original tab will produce the
// same secure DNS error, triggering a second captive portal check.
Login(browser_list_->get(1), 0 /* num_loading_tabs */,
1 /* num_timed_out_tabs */, 2 /* expected_portal_checks */);
// The reload of the original page should have produced another DNS error
// page.
EXPECT_TRUE(broken_tab_contents->GetController()
.GetLastCommittedEntry()
->GetPageType() == content::PAGE_TYPE_ERROR);
}
// An HTTPS load happens slowly. The reloader triggers a captive portal check,
// which finds a captive portal. The HTTPS load finally completes with a secure
// DNS error, which does not trigger another captive portal check. Only one
// login tab should exist.
IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest,
SlowLoadSecureDnsErrorWithCaptivePortal) {
PrefService* local_state = g_browser_process->local_state();
local_state->SetString(prefs::kDnsOverHttpsTemplates,
"https://bar.test/dns-query{?dns}");
local_state->SetString(prefs::kDnsOverHttpsMode,
chrome_browser_net::kDnsOverHttpsModeSecure);
SlowLoadBehindCaptivePortal(browser(), true /* expect_open_login_tab */,
true /* expect_new_login_browser */);
// Connection finally hits a secure DNS error. No new captive portal check is
// triggered.
MultiNavigationObserver navigation_observer;
FailJobs(1, net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(net::ERR_CERT_COMMON_NAME_INVALID, true));
navigation_observer.WaitForNavigations(1);
WebContents* tab =
browser_list_->get(0)->tab_strip_model()->GetWebContentsAt(0);
EXPECT_EQ(1, navigation_observer.NumNavigationsForTab(tab));
EXPECT_TRUE(tab->GetController().GetLastCommittedEntry()->GetPageType() ==
content::PAGE_TYPE_ERROR);
EXPECT_EQ(2u, browser_list_->size());
EXPECT_EQ(2, NumTabs());
}
// An HTTPS load happens slowly. The reloader triggers a captive portal check,
// which finds a captive portal. After logging in, the HTTPS load finally
// completes with a secure DNS error, which triggers another captive portal
// check that should succeed.
IN_PROC_BROWSER_TEST_F(CaptivePortalBrowserTest,
SlowLoadSecureDnsErrorAfterLogin) {
PrefService* local_state = g_browser_process->local_state();
local_state->SetString(prefs::kDnsOverHttpsTemplates,
"https://bar.test/dns-query{?dns}");
local_state->SetString(prefs::kDnsOverHttpsMode,
chrome_browser_net::kDnsOverHttpsModeSecure);
SlowLoadBehindCaptivePortal(browser(), true /* expect_open_login_tab */,
true /* expect_new_login_browser */);
// Login to the captive portal.
Login(browser_list_->get(1), 1 /* num_loading_tabs */,
0 /* num_timed_out_tabs */, 1 /* expected_portal_checks */);
// Connection finally hits a secure DNS error. It should reload without
// sending a new captive portal check.
MultiNavigationObserver navigation_observer;
FailJobs(1, net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(net::ERR_CERT_COMMON_NAME_INVALID, true));
navigation_observer.WaitForNavigations(1);
WebContents* tab =
browser_list_->get(0)->tab_strip_model()->GetWebContentsAt(0);
EXPECT_EQ(1, navigation_observer.NumNavigationsForTab(tab));
EXPECT_TRUE(tab->GetController().GetLastCommittedEntry()->GetPageType() ==
content::PAGE_TYPE_NORMAL);
EXPECT_EQ(2u, browser_list_->size());
EXPECT_EQ(2, NumTabs());
} }
...@@ -172,7 +172,10 @@ bool ShouldUseFixUrlServiceForError(const error_page::Error& error, ...@@ -172,7 +172,10 @@ bool ShouldUseFixUrlServiceForError(const error_page::Error& error,
*error_param = "http404"; *error_param = "http404";
return true; return true;
} }
if (IsNetDnsError(error)) { // Don't use the link doctor for secure DNS network errors, since the
// additional navigation may interfere with the captive portal probe state.
if (IsNetDnsError(error) &&
!error.resolve_error_info().is_secure_network_error) {
*error_param = "dnserror"; *error_param = "dnserror";
return true; return true;
} }
...@@ -475,7 +478,13 @@ bool NetErrorHelperCore::IsReloadableError( ...@@ -475,7 +478,13 @@ bool NetErrorHelperCore::IsReloadableError(
info.error.reason() != net::ERR_INVALID_AUTH_CREDENTIALS && info.error.reason() != net::ERR_INVALID_AUTH_CREDENTIALS &&
// Don't auto-reload non-http/https schemas. // Don't auto-reload non-http/https schemas.
// https://crbug.com/471713 // https://crbug.com/471713
url.SchemeIsHTTPOrHTTPS(); url.SchemeIsHTTPOrHTTPS() &&
// Don't auto reload if the error was a secure DNS network error, since
// the reload may interfere with the captive portal probe state.
// TODO(crbug.com/1016164): Explore how to allow reloads for secure DNS
// network errors without interfering with the captive portal probe
// state.
!info.error.resolve_error_info().is_secure_network_error;
} }
NetErrorHelperCore::NetErrorHelperCore(Delegate* delegate, NetErrorHelperCore::NetErrorHelperCore(Delegate* delegate,
......
...@@ -122,7 +122,8 @@ void CaptivePortalTabHelper::DidFinishNavigation( ...@@ -122,7 +122,8 @@ void CaptivePortalTabHelper::DidFinishNavigation(
} }
if (navigation_handle->HasCommitted()) { if (navigation_handle->HasCommitted()) {
tab_reloader_->OnLoadCommitted(navigation_handle->GetNetErrorCode()); tab_reloader_->OnLoadCommitted(navigation_handle->GetNetErrorCode(),
navigation_handle->GetResolveErrorInfo());
} else { } else {
tab_reloader_->OnAbort(); tab_reloader_->OnAbort();
} }
......
...@@ -45,7 +45,7 @@ class MockCaptivePortalTabReloader : public CaptivePortalTabReloader { ...@@ -45,7 +45,7 @@ class MockCaptivePortalTabReloader : public CaptivePortalTabReloader {
: CaptivePortalTabReloader(nullptr, nullptr, base::Callback<void()>()) {} : CaptivePortalTabReloader(nullptr, nullptr, base::Callback<void()>()) {}
MOCK_METHOD1(OnLoadStart, void(bool)); MOCK_METHOD1(OnLoadStart, void(bool));
MOCK_METHOD1(OnLoadCommitted, void(int)); MOCK_METHOD2(OnLoadCommitted, void(int, net::ResolveErrorInfo));
MOCK_METHOD0(OnAbort, void()); MOCK_METHOD0(OnAbort, void());
MOCK_METHOD1(OnRedirect, void(bool)); MOCK_METHOD1(OnRedirect, void(bool));
MOCK_METHOD2(OnCaptivePortalResults, MOCK_METHOD2(OnCaptivePortalResults,
...@@ -91,7 +91,9 @@ class CaptivePortalTabHelperTest : public content::RenderViewHostTestHarness { ...@@ -91,7 +91,9 @@ class CaptivePortalTabHelperTest : public content::RenderViewHostTestHarness {
auto navigation = content::NavigationSimulator::CreateBrowserInitiated( auto navigation = content::NavigationSimulator::CreateBrowserInitiated(
url, web_contents()); url, web_contents());
navigation->Start(); navigation->Start();
EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1); EXPECT_CALL(mock_reloader(),
OnLoadCommitted(net::OK, net::ResolveErrorInfo(net::OK)))
.Times(1);
navigation->Commit(); navigation->Commit();
} }
...@@ -102,7 +104,27 @@ class CaptivePortalTabHelperTest : public content::RenderViewHostTestHarness { ...@@ -102,7 +104,27 @@ class CaptivePortalTabHelperTest : public content::RenderViewHostTestHarness {
auto navigation = content::NavigationSimulator::CreateBrowserInitiated( auto navigation = content::NavigationSimulator::CreateBrowserInitiated(
url, web_contents()); url, web_contents());
navigation->Fail(net::ERR_TIMED_OUT); navigation->Fail(net::ERR_TIMED_OUT);
EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_TIMED_OUT)).Times(1); EXPECT_CALL(
mock_reloader(),
OnLoadCommitted(net::ERR_TIMED_OUT, net::ResolveErrorInfo(net::OK)))
.Times(1);
navigation->CommitErrorPage();
}
// Simulates a secure DNS network error while requesting |url|.
void SimulateSecureDnsNetworkError(const GURL& url) {
EXPECT_CALL(mock_reloader(), OnLoadStart(url.SchemeIsCryptographic()))
.Times(1);
auto navigation = content::NavigationSimulator::CreateBrowserInitiated(
url, web_contents());
navigation->SetResolveErrorInfo({net::ERR_CERT_COMMON_NAME_INVALID,
true /* is_secure_network_error */});
navigation->Fail(net::ERR_NAME_NOT_RESOLVED);
EXPECT_CALL(mock_reloader(),
OnLoadCommitted(net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(
net::ERR_CERT_COMMON_NAME_INVALID, true)))
.Times(1);
navigation->CommitErrorPage(); navigation->CommitErrorPage();
} }
...@@ -189,6 +211,13 @@ TEST_F(CaptivePortalTabHelperTest, HttpsTimeout) { ...@@ -189,6 +211,13 @@ TEST_F(CaptivePortalTabHelperTest, HttpsTimeout) {
EXPECT_FALSE(tab_helper()->IsLoginTab()); EXPECT_FALSE(tab_helper()->IsLoginTab());
} }
TEST_F(CaptivePortalTabHelperTest, HttpsSecureDnsNetworkError) {
SimulateSecureDnsNetworkError(GURL(kHttpsUrl));
// Make sure no state was carried over from the secure DNS network error.
SimulateSuccess(GURL(kHttpsUrl));
EXPECT_FALSE(tab_helper()->IsLoginTab());
}
TEST_F(CaptivePortalTabHelperTest, HttpsAbort) { TEST_F(CaptivePortalTabHelperTest, HttpsAbort) {
SimulateAbort(GURL(kHttpsUrl)); SimulateAbort(GURL(kHttpsUrl));
// Make sure no state was carried over from the abort. // Make sure no state was carried over from the abort.
...@@ -261,7 +290,9 @@ TEST_F(CaptivePortalTabHelperTest, UnexpectedProvisionalLoad) { ...@@ -261,7 +290,9 @@ TEST_F(CaptivePortalTabHelperTest, UnexpectedProvisionalLoad) {
// The cross-process navigation fails. // The cross-process navigation fails.
cross_process_navigation->Fail(net::ERR_FAILED); cross_process_navigation->Fail(net::ERR_FAILED);
EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_FAILED)).Times(1); EXPECT_CALL(mock_reloader(),
OnLoadCommitted(net::ERR_FAILED, net::ResolveErrorInfo(net::OK)))
.Times(1);
cross_process_navigation->CommitErrorPage(); cross_process_navigation->CommitErrorPage();
} }
...@@ -298,7 +329,9 @@ TEST_F(CaptivePortalTabHelperTest, UnexpectedCommit) { ...@@ -298,7 +329,9 @@ TEST_F(CaptivePortalTabHelperTest, UnexpectedCommit) {
EXPECT_CALL(mock_reloader(), EXPECT_CALL(mock_reloader(),
OnLoadStart(same_site_url.SchemeIsCryptographic())) OnLoadStart(same_site_url.SchemeIsCryptographic()))
.Times(1); .Times(1);
EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1); EXPECT_CALL(mock_reloader(),
OnLoadCommitted(net::OK, net::ResolveErrorInfo(net::OK)))
.Times(1);
same_site_navigation->Commit(); same_site_navigation->Commit();
} }
...@@ -355,7 +388,9 @@ TEST_F(CaptivePortalTabHelperTest, HttpsSubframeParallelError) { ...@@ -355,7 +388,9 @@ TEST_F(CaptivePortalTabHelperTest, HttpsSubframeParallelError) {
// Error page load finishes. // Error page load finishes.
subframe_navigation->CommitErrorPage(); subframe_navigation->CommitErrorPage();
EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_UNEXPECTED)).Times(1); EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_UNEXPECTED,
net::ResolveErrorInfo(net::OK)))
.Times(1);
main_frame_navigation->CommitErrorPage(); main_frame_navigation->CommitErrorPage();
} }
...@@ -373,7 +408,9 @@ TEST_F(CaptivePortalTabHelperTest, HttpToHttpsRedirectTimeout) { ...@@ -373,7 +408,9 @@ TEST_F(CaptivePortalTabHelperTest, HttpToHttpsRedirectTimeout) {
navigation->Fail(net::ERR_TIMED_OUT); navigation->Fail(net::ERR_TIMED_OUT);
EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_TIMED_OUT)).Times(1); EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::ERR_TIMED_OUT,
net::ResolveErrorInfo(net::OK)))
.Times(1);
navigation->CommitErrorPage(); navigation->CommitErrorPage();
} }
...@@ -391,7 +428,9 @@ TEST_F(CaptivePortalTabHelperTest, HttpsToHttpRedirect) { ...@@ -391,7 +428,9 @@ TEST_F(CaptivePortalTabHelperTest, HttpsToHttpRedirect) {
.Times(1); .Times(1);
navigation->Redirect(http_url); navigation->Redirect(http_url);
EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1); EXPECT_CALL(mock_reloader(),
OnLoadCommitted(net::OK, net::ResolveErrorInfo(net::OK)))
.Times(1);
navigation->Commit(); navigation->Commit();
} }
...@@ -408,7 +447,9 @@ TEST_F(CaptivePortalTabHelperTest, HttpToHttpRedirect) { ...@@ -408,7 +447,9 @@ TEST_F(CaptivePortalTabHelperTest, HttpToHttpRedirect) {
.Times(1); .Times(1);
navigation->Redirect(http_url); navigation->Redirect(http_url);
EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1); EXPECT_CALL(mock_reloader(),
OnLoadCommitted(net::OK, net::ResolveErrorInfo(net::OK)))
.Times(1);
navigation->Commit(); navigation->Commit();
} }
...@@ -431,7 +472,9 @@ TEST_F(CaptivePortalTabHelperTest, SubframeRedirect) { ...@@ -431,7 +472,9 @@ TEST_F(CaptivePortalTabHelperTest, SubframeRedirect) {
GURL https_url(kHttpsUrl); GURL https_url(kHttpsUrl);
subframe_navigation->Redirect(https_url); subframe_navigation->Redirect(https_url);
EXPECT_CALL(mock_reloader(), OnLoadCommitted(net::OK)).Times(1); EXPECT_CALL(mock_reloader(),
OnLoadCommitted(net::OK, net::ResolveErrorInfo(net::OK)))
.Times(1);
main_frame_navigation->Commit(); main_frame_navigation->Commit();
} }
......
...@@ -70,10 +70,18 @@ void CaptivePortalTabReloader::OnLoadStart(bool is_ssl) { ...@@ -70,10 +70,18 @@ void CaptivePortalTabReloader::OnLoadStart(bool is_ssl) {
SetState(STATE_TIMER_RUNNING); SetState(STATE_TIMER_RUNNING);
} }
void CaptivePortalTabReloader::OnLoadCommitted(int net_error) { void CaptivePortalTabReloader::OnLoadCommitted(
int net_error,
net::ResolveErrorInfo resolve_error_info) {
provisional_main_frame_load_ = false; provisional_main_frame_load_ = false;
ssl_url_in_redirect_chain_ = false; ssl_url_in_redirect_chain_ = false;
// There was a secure DNS network error, so maybe check for a captive portal.
if (resolve_error_info.is_secure_network_error) {
OnSecureDnsNetworkError();
return;
}
if (state_ == STATE_NONE) if (state_ == STATE_NONE)
return; return;
...@@ -181,6 +189,19 @@ void CaptivePortalTabReloader::OnSlowSSLConnect() { ...@@ -181,6 +189,19 @@ void CaptivePortalTabReloader::OnSlowSSLConnect() {
SetState(STATE_MAYBE_BROKEN_BY_PORTAL); SetState(STATE_MAYBE_BROKEN_BY_PORTAL);
} }
void CaptivePortalTabReloader::OnSecureDnsNetworkError() {
if (state_ == STATE_NONE || state_ == STATE_TIMER_RUNNING) {
SetState(STATE_MAYBE_BROKEN_BY_PORTAL);
return;
}
if (state_ == STATE_NEEDS_RELOAD) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&CaptivePortalTabReloader::ReloadTabIfNeeded,
weak_factory_.GetWeakPtr()));
}
}
void CaptivePortalTabReloader::SetState(State new_state) { void CaptivePortalTabReloader::SetState(State new_state) {
// Stop the timer even when old and new states are the same. // Stop the timer even when old and new states are the same.
if (state_ == STATE_TIMER_RUNNING) { if (state_ == STATE_TIMER_RUNNING) {
...@@ -192,7 +213,8 @@ void CaptivePortalTabReloader::SetState(State new_state) { ...@@ -192,7 +213,8 @@ void CaptivePortalTabReloader::SetState(State new_state) {
// Check for unexpected state transitions. // Check for unexpected state transitions.
switch (state_) { switch (state_) {
case STATE_NONE: case STATE_NONE:
DCHECK(new_state == STATE_NONE || new_state == STATE_TIMER_RUNNING); DCHECK(new_state == STATE_NONE || new_state == STATE_TIMER_RUNNING ||
new_state == STATE_MAYBE_BROKEN_BY_PORTAL);
break; break;
case STATE_TIMER_RUNNING: case STATE_TIMER_RUNNING:
DCHECK(new_state == STATE_NONE || DCHECK(new_state == STATE_NONE ||
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "components/captive_portal/content/captive_portal_service.h" #include "components/captive_portal/content/captive_portal_service.h"
#include "net/dns/public/resolve_error_info.h"
namespace content { namespace content {
class WebContents; class WebContents;
...@@ -85,15 +86,17 @@ class CaptivePortalTabReloader { ...@@ -85,15 +86,17 @@ class CaptivePortalTabReloader {
// loads and for error pages. // loads and for error pages.
virtual void OnLoadStart(bool is_ssl); virtual void OnLoadStart(bool is_ssl);
// Called when the main frame is committed. |net_error| will be net::OK in // Called when the main frame is committed. |net_error| will be net::OK in
// the case of a successful load. For an errror page, the entire 3-step // the case of a successful load. |resolve_error_info| contains information
// about any hostname resolution error. For an error page, the entire 3-step
// process of getting the error, starting a new provisional load for the error // process of getting the error, starting a new provisional load for the error
// page, and committing the error page is treated as a single commit. // page, and committing the error page is treated as a single commit.
// //
// The Link Doctor page will typically be one OnLoadCommitted with an error // The Link Doctor page will typically be one OnLoadCommitted with an error
// code, followed by another OnLoadCommitted with net::OK for the Link Doctor // code, followed by another OnLoadCommitted with net::OK for the Link Doctor
// page. // page.
virtual void OnLoadCommitted(int net_error); virtual void OnLoadCommitted(int net_error,
net::ResolveErrorInfo resolve_error_info);
// This is called when the current provisional main frame load is canceled. // This is called when the current provisional main frame load is canceled.
// Sets state to STATE_NONE, unless this is a login tab. // Sets state to STATE_NONE, unless this is a login tab.
...@@ -138,6 +141,9 @@ class CaptivePortalTabReloader { ...@@ -138,6 +141,9 @@ class CaptivePortalTabReloader {
// while to commit. // while to commit.
void OnSlowSSLConnect(); void OnSlowSSLConnect();
// Called when a main frame loads with a secure DNS network error.
void OnSecureDnsNetworkError();
// Reloads the tab if there's no provisional load going on and the current // Reloads the tab if there's no provisional load going on and the current
// state is STATE_NEEDS_RELOAD. Not safe to call synchronously when called // state is STATE_NEEDS_RELOAD. Not safe to call synchronously when called
// by from a WebContentsObserver function, since the WebContents is currently // by from a WebContentsObserver function, since the WebContents is currently
......
...@@ -122,7 +122,7 @@ TEST_F(CaptivePortalTabReloaderTest, InternetConnected) { ...@@ -122,7 +122,7 @@ TEST_F(CaptivePortalTabReloaderTest, InternetConnected) {
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state()); EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
EXPECT_FALSE(tab_reloader().TimerRunning()); EXPECT_FALSE(tab_reloader().TimerRunning());
tab_reloader().OnLoadCommitted(net::OK); tab_reloader().OnLoadCommitted(net::OK, net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state()); EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
} }
...@@ -138,7 +138,8 @@ TEST_F(CaptivePortalTabReloaderTest, InternetConnectedTimeout) { ...@@ -138,7 +138,8 @@ TEST_F(CaptivePortalTabReloaderTest, InternetConnectedTimeout) {
EXPECT_TRUE(tab_reloader().TimerRunning()); EXPECT_TRUE(tab_reloader().TimerRunning());
EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(1); EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(1);
tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT); tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT,
net::ResolveErrorInfo(net::OK));
EXPECT_FALSE(tab_reloader().TimerRunning()); EXPECT_FALSE(tab_reloader().TimerRunning());
EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL, EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL,
tab_reloader().state()); tab_reloader().state());
...@@ -171,12 +172,12 @@ TEST_F(CaptivePortalTabReloaderTest, NoResponse) { ...@@ -171,12 +172,12 @@ TEST_F(CaptivePortalTabReloaderTest, NoResponse) {
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state()); EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
EXPECT_FALSE(tab_reloader().TimerRunning()); EXPECT_FALSE(tab_reloader().TimerRunning());
tab_reloader().OnLoadCommitted(net::OK); tab_reloader().OnLoadCommitted(net::OK, net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state()); EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
} }
// Simulates a slow HTTP load when behind a captive portal, that eventually. // Simulates a slow HTTP load when behind a captive portal, that eventually.
// tiems out. Since it's HTTP, the TabReloader should do nothing. // times out. Since it's HTTP, the TabReloader should do nothing.
TEST_F(CaptivePortalTabReloaderTest, DoesNothingOnHttp) { TEST_F(CaptivePortalTabReloaderTest, DoesNothingOnHttp) {
tab_reloader().OnLoadStart(false); tab_reloader().OnLoadStart(false);
EXPECT_FALSE(tab_reloader().TimerRunning()); EXPECT_FALSE(tab_reloader().TimerRunning());
...@@ -194,7 +195,8 @@ TEST_F(CaptivePortalTabReloaderTest, DoesNothingOnHttp) { ...@@ -194,7 +195,8 @@ TEST_F(CaptivePortalTabReloaderTest, DoesNothingOnHttp) {
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state()); EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
// The page times out. // The page times out.
tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT); tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT,
net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state()); EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
} }
...@@ -227,7 +229,8 @@ TEST_F(CaptivePortalTabReloaderTest, Login) { ...@@ -227,7 +229,8 @@ TEST_F(CaptivePortalTabReloaderTest, Login) {
tab_reloader().state()); tab_reloader().state());
// The error page commits, which should start an asynchronous reload. // The error page commits, which should start an asynchronous reload.
tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT); tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT,
net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD, EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD,
tab_reloader().state()); tab_reloader().state());
...@@ -258,7 +261,8 @@ TEST_F(CaptivePortalTabReloaderTest, LoginLate) { ...@@ -258,7 +261,8 @@ TEST_F(CaptivePortalTabReloaderTest, LoginLate) {
EXPECT_FALSE(tab_reloader().TimerRunning()); EXPECT_FALSE(tab_reloader().TimerRunning());
// The error page commits. // The error page commits.
tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT); tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT,
net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL, EXPECT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL,
tab_reloader().state()); tab_reloader().state());
...@@ -277,7 +281,8 @@ TEST_F(CaptivePortalTabReloaderTest, TimeoutFast) { ...@@ -277,7 +281,8 @@ TEST_F(CaptivePortalTabReloaderTest, TimeoutFast) {
// The error page commits, which should trigger a captive portal check, // The error page commits, which should trigger a captive portal check,
// since the timer's still running. // since the timer's still running.
EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(1); EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(1);
tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT); tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT,
net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL, EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL,
tab_reloader().state()); tab_reloader().state());
...@@ -299,6 +304,199 @@ TEST_F(CaptivePortalTabReloaderTest, TimeoutFast) { ...@@ -299,6 +304,199 @@ TEST_F(CaptivePortalTabReloaderTest, TimeoutFast) {
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state()); EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
} }
// The secure DNS config is misconfigured. A secure DNS network error on a
// HTTP navigation triggers a captive portal probe. The probe does not find
// a captive portal.
TEST_F(CaptivePortalTabReloaderTest, HttpBadSecureDnsConfig) {
tab_reloader().OnLoadStart(false);
EXPECT_FALSE(tab_reloader().TimerRunning());
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
// The page encounters a secure DNS network error. The error page commits,
// which should trigger a captive portal check, even for HTTP pages.
EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(1);
tab_reloader().OnLoadCommitted(
net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(net::ERR_CERT_COMMON_NAME_INVALID,
true /* is_secure_network_error */));
EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL,
tab_reloader().state());
// If the only issue was the secure DNS config not being valid, the probes
// (which disable secure DNS) should indicate an internet connection.
tab_reloader().OnCaptivePortalResults(
captive_portal::RESULT_INTERNET_CONNECTED,
captive_portal::RESULT_INTERNET_CONNECTED);
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
}
// The secure DNS config is misconfigured. A secure DNS network error on a
// HTTPS navigation triggers a captive portal probe before the SSL timer
// triggers. The probe does not find a captive portal.
TEST_F(CaptivePortalTabReloaderTest,
HttpsBadSecureDnsConfigPageLoadsBeforeTimerTriggers) {
tab_reloader().OnLoadStart(true);
EXPECT_EQ(CaptivePortalTabReloader::STATE_TIMER_RUNNING,
tab_reloader().state());
EXPECT_TRUE(tab_reloader().TimerRunning());
// The page encounters a secure DNS network error. The error page commits,
// which should trigger a captive portal check. The SSL timer should be
// cancelled.
EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(1);
tab_reloader().OnLoadCommitted(
net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(net::ERR_CERT_COMMON_NAME_INVALID,
true /* is_secure_network_error */));
EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL,
tab_reloader().state());
EXPECT_FALSE(tab_reloader().TimerRunning());
// If the only issue was the secure DNS config not being valid, the probes
// (which disable secure DNS) should indicate an internet connection.
tab_reloader().OnCaptivePortalResults(
captive_portal::RESULT_INTERNET_CONNECTED,
captive_portal::RESULT_INTERNET_CONNECTED);
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
}
// The secure DNS config is misconfigured. The SSL timer triggers a captive
// portal probe, which does not complete before the page loads with a secure
// DNS network error. The probe does not find a captive portal.
TEST_F(CaptivePortalTabReloaderTest,
HttpsBadSecureDnsConfigPageLoadsBeforeTimerTriggeredResults) {
tab_reloader().OnLoadStart(true);
EXPECT_EQ(CaptivePortalTabReloader::STATE_TIMER_RUNNING,
tab_reloader().state());
EXPECT_TRUE(tab_reloader().TimerRunning());
EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(1);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(tab_reloader().TimerRunning());
EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL,
tab_reloader().state());
// The page encounters a secure DNS network error. The error page commits.
// Since a probe is already scheduled, we don't schedule another one.
tab_reloader().OnLoadCommitted(
net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(net::ERR_CERT_COMMON_NAME_INVALID,
true /* is_secure_network_error */));
EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL,
tab_reloader().state());
// If the only issue was the secure DNS config not being valid, the probes
// (which disable secure DNS) should indicate an internet connection.
tab_reloader().OnCaptivePortalResults(
captive_portal::RESULT_INTERNET_CONNECTED,
captive_portal::RESULT_INTERNET_CONNECTED);
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
}
// The secure DNS config is misconfigured. The SSL timer triggers a captive
// portal probe, which completes before the page loads with a secure DNS
// network error, which triggers another captive portal probe. The probe does
// not find a captive portal.
TEST_F(CaptivePortalTabReloaderTest,
HttpsBadSecureDnsConfigPageLoadsAfterTimerTriggeredResults) {
tab_reloader().OnLoadStart(true);
EXPECT_EQ(CaptivePortalTabReloader::STATE_TIMER_RUNNING,
tab_reloader().state());
EXPECT_TRUE(tab_reloader().TimerRunning());
EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(2);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(tab_reloader().TimerRunning());
EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL,
tab_reloader().state());
// If the only issue was the secure DNS config not being valid, the probes
// (which disable secure DNS) should indicate an internet connection.
tab_reloader().OnCaptivePortalResults(
captive_portal::RESULT_INTERNET_CONNECTED,
captive_portal::RESULT_INTERNET_CONNECTED);
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
// The page encounters a secure DNS network error. The error page commits,
// which triggers another captive portal check.
tab_reloader().OnLoadCommitted(
net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(net::ERR_CERT_COMMON_NAME_INVALID,
true /* is_secure_network_error */));
EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL,
tab_reloader().state());
}
// The secure DNS config is configured correctly. The SSL timer triggers a
// captive portal probe. This probe finds a captive portal and completes before
// the page loads with a secure DNS network error, which does not trigger
// another captive portal probe. The user then logs in, causing a page reload.
TEST_F(CaptivePortalTabReloaderTest,
HttpsSecureDnsConfigPageLoadsAfterTimerTriggeredResults) {
tab_reloader().OnLoadStart(true);
EXPECT_EQ(CaptivePortalTabReloader::STATE_TIMER_RUNNING,
tab_reloader().state());
EXPECT_TRUE(tab_reloader().TimerRunning());
EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(1);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(tab_reloader().TimerRunning());
EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL,
tab_reloader().state());
// The probe finds a captive portal and opens a login page.
EXPECT_CALL(tab_reloader(), MaybeOpenCaptivePortalLoginTab()).Times(1);
tab_reloader().OnCaptivePortalResults(
captive_portal::RESULT_INTERNET_CONNECTED,
captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL);
EXPECT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL,
tab_reloader().state());
EXPECT_FALSE(tab_reloader().TimerRunning());
// The original navigation encounters a secure DNS network error. The error
// page commits but does not trigger another captive portal check.
tab_reloader().OnLoadCommitted(
net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(net::ERR_CERT_COMMON_NAME_INVALID,
true /* is_secure_network_error */));
EXPECT_EQ(CaptivePortalTabReloader::STATE_BROKEN_BY_PORTAL,
tab_reloader().state());
// The user logs on from another tab, and the page is reloaded.
EXPECT_CALL(tab_reloader(), ReloadTab()).Times(1);
tab_reloader().OnCaptivePortalResults(
captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL,
captive_portal::RESULT_INTERNET_CONNECTED);
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
}
// The user logs in in a different tab, before the page loads with a secure
// DNS network error. A reload should occur when the page commits.
TEST_F(CaptivePortalTabReloaderTest, HttpsSecureDnsConfigErrorAlreadyLoggedIn) {
tab_reloader().OnLoadStart(true);
// The user logs in from another tab before the tab errors out.
tab_reloader().OnCaptivePortalResults(
captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL,
captive_portal::RESULT_INTERNET_CONNECTED);
EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD,
tab_reloader().state());
// The error page commits, which should trigger a reload.
EXPECT_CALL(tab_reloader(), ReloadTab()).Times(1);
tab_reloader().OnLoadCommitted(
net::ERR_NAME_NOT_RESOLVED,
net::ResolveErrorInfo(net::ERR_CERT_COMMON_NAME_INVALID,
true /* is_secure_network_error */));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
}
// An SSL protocol error triggers a captive portal check behind a captive // An SSL protocol error triggers a captive portal check behind a captive
// portal. The user then logs in. // portal. The user then logs in.
TEST_F(CaptivePortalTabReloaderTest, SSLProtocolError) { TEST_F(CaptivePortalTabReloaderTest, SSLProtocolError) {
...@@ -307,7 +505,8 @@ TEST_F(CaptivePortalTabReloaderTest, SSLProtocolError) { ...@@ -307,7 +505,8 @@ TEST_F(CaptivePortalTabReloaderTest, SSLProtocolError) {
// The error page commits, which should trigger a captive portal check, // The error page commits, which should trigger a captive portal check,
// since the timer's still running. // since the timer's still running.
EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(1); EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(1);
tab_reloader().OnLoadCommitted(net::ERR_SSL_PROTOCOL_ERROR); tab_reloader().OnLoadCommitted(net::ERR_SSL_PROTOCOL_ERROR,
net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL, EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL,
tab_reloader().state()); tab_reloader().state());
...@@ -338,7 +537,8 @@ TEST_F(CaptivePortalTabReloaderTest, SSLProtocolErrorFastLogin) { ...@@ -338,7 +537,8 @@ TEST_F(CaptivePortalTabReloaderTest, SSLProtocolErrorFastLogin) {
// The error page commits, which should trigger a captive portal check, // The error page commits, which should trigger a captive portal check,
// since the timer's still running. // since the timer's still running.
EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(1); EXPECT_CALL(tab_reloader(), CheckForCaptivePortal()).Times(1);
tab_reloader().OnLoadCommitted(net::ERR_SSL_PROTOCOL_ERROR); tab_reloader().OnLoadCommitted(net::ERR_SSL_PROTOCOL_ERROR,
net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL, EXPECT_EQ(CaptivePortalTabReloader::STATE_MAYBE_BROKEN_BY_PORTAL,
tab_reloader().state()); tab_reloader().state());
...@@ -365,7 +565,8 @@ TEST_F(CaptivePortalTabReloaderTest, SSLProtocolErrorAlreadyLoggedIn) { ...@@ -365,7 +565,8 @@ TEST_F(CaptivePortalTabReloaderTest, SSLProtocolErrorAlreadyLoggedIn) {
// The error page commits, which should trigger a reload. // The error page commits, which should trigger a reload.
EXPECT_CALL(tab_reloader(), ReloadTab()).Times(1); EXPECT_CALL(tab_reloader(), ReloadTab()).Times(1);
tab_reloader().OnLoadCommitted(net::ERR_SSL_PROTOCOL_ERROR); tab_reloader().OnLoadCommitted(net::ERR_SSL_PROTOCOL_ERROR,
net::ResolveErrorInfo(net::OK));
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state()); EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
} }
...@@ -391,7 +592,8 @@ TEST_F(CaptivePortalTabReloaderTest, AlreadyLoggedIn) { ...@@ -391,7 +592,8 @@ TEST_F(CaptivePortalTabReloaderTest, AlreadyLoggedIn) {
tab_reloader().state()); tab_reloader().state());
// The error page commits, which should start an asynchronous reload. // The error page commits, which should start an asynchronous reload.
tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT); tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT,
net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD, EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD,
tab_reloader().state()); tab_reloader().state());
...@@ -416,7 +618,8 @@ TEST_F(CaptivePortalTabReloaderTest, AlreadyLoggedInBeforeTimerTriggers) { ...@@ -416,7 +618,8 @@ TEST_F(CaptivePortalTabReloaderTest, AlreadyLoggedInBeforeTimerTriggers) {
EXPECT_FALSE(tab_reloader().TimerRunning()); EXPECT_FALSE(tab_reloader().TimerRunning());
// The error page commits, which should start an asynchronous reload. // The error page commits, which should start an asynchronous reload.
tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT); tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT,
net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD, EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD,
tab_reloader().state()); tab_reloader().state());
...@@ -441,7 +644,8 @@ TEST_F(CaptivePortalTabReloaderTest, LoginWhileTimerRunning) { ...@@ -441,7 +644,8 @@ TEST_F(CaptivePortalTabReloaderTest, LoginWhileTimerRunning) {
tab_reloader().state()); tab_reloader().state());
// The error page commits, which should start an asynchronous reload. // The error page commits, which should start an asynchronous reload.
tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT); tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT,
net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD, EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD,
tab_reloader().state()); tab_reloader().state());
...@@ -491,7 +695,8 @@ TEST_F(CaptivePortalTabReloaderTest, BehindPortalResultWhileTimerRunning) { ...@@ -491,7 +695,8 @@ TEST_F(CaptivePortalTabReloaderTest, BehindPortalResultWhileTimerRunning) {
tab_reloader().state()); tab_reloader().state());
// The error page commits, which should start an asynchronous reload. // The error page commits, which should start an asynchronous reload.
tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT); tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT,
net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD, EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD,
tab_reloader().state()); tab_reloader().state());
...@@ -518,7 +723,7 @@ TEST_F(CaptivePortalTabReloaderTest, LogInWhileTimerRunningNoError) { ...@@ -518,7 +723,7 @@ TEST_F(CaptivePortalTabReloaderTest, LogInWhileTimerRunningNoError) {
tab_reloader().state()); tab_reloader().state());
// The page successfully commits, so no reload is triggered. // The page successfully commits, so no reload is triggered.
tab_reloader().OnLoadCommitted(net::OK); tab_reloader().OnLoadCommitted(net::OK, net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state()); EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
} }
...@@ -579,7 +784,7 @@ TEST_F(CaptivePortalTabReloaderTest, HttpToHttpsRedirectInternetConnected) { ...@@ -579,7 +784,7 @@ TEST_F(CaptivePortalTabReloaderTest, HttpToHttpsRedirectInternetConnected) {
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state()); EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
EXPECT_FALSE(tab_reloader().TimerRunning()); EXPECT_FALSE(tab_reloader().TimerRunning());
tab_reloader().OnLoadCommitted(net::OK); tab_reloader().OnLoadCommitted(net::OK, net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state()); EXPECT_EQ(CaptivePortalTabReloader::STATE_NONE, tab_reloader().state());
} }
...@@ -619,7 +824,8 @@ TEST_F(CaptivePortalTabReloaderTest, HttpToHttpsRedirectLogin) { ...@@ -619,7 +824,8 @@ TEST_F(CaptivePortalTabReloaderTest, HttpToHttpsRedirectLogin) {
tab_reloader().state()); tab_reloader().state());
// The error page commits, which should start an asynchronous reload. // The error page commits, which should start an asynchronous reload.
tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT); tab_reloader().OnLoadCommitted(net::ERR_CONNECTION_TIMED_OUT,
net::ResolveErrorInfo(net::OK));
EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD, EXPECT_EQ(CaptivePortalTabReloader::STATE_NEEDS_RELOAD,
tab_reloader().state()); tab_reloader().state());
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "content/public/browser/navigation_throttle.h" #include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/reload_type.h" #include "content/public/browser/reload_type.h"
#include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_receiver.h"
#include "net/dns/public/resolve_error_info.h"
#include "services/service_manager/public/cpp/interface_provider.h" #include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/public/mojom/referrer.mojom.h" #include "third_party/blink/public/mojom/referrer.mojom.h"
#include "ui/base/page_transition_types.h" #include "ui/base/page_transition_types.h"
...@@ -296,6 +297,10 @@ class NavigationSimulator { ...@@ -296,6 +297,10 @@ class NavigationSimulator {
// in throttles deferring the navigation with a call to Wait(). // in throttles deferring the navigation with a call to Wait().
virtual void SetAutoAdvance(bool auto_advance) = 0; virtual void SetAutoAdvance(bool auto_advance) = 0;
// Sets the ResolveErrorInfo to be set on the URLLoaderCompletionStatus.
virtual void SetResolveErrorInfo(
const net::ResolveErrorInfo& resolve_error_info) = 0;
// Sets the SSLInfo to be set on the response. This should be called before // Sets the SSLInfo to be set on the response. This should be called before
// Commit(). // Commit().
virtual void SetSSLInfo(const net::SSLInfo& ssl_info) = 0; virtual void SetSSLInfo(const net::SSLInfo& ssl_info) = 0;
......
...@@ -684,6 +684,7 @@ void NavigationSimulatorImpl::FailWithResponseHeaders( ...@@ -684,6 +684,7 @@ void NavigationSimulatorImpl::FailWithResponseHeaders(
static_cast<TestNavigationURLLoader*>(request_->loader_for_testing()); static_cast<TestNavigationURLLoader*>(request_->loader_for_testing());
CHECK(url_loader); CHECK(url_loader);
network::URLLoaderCompletionStatus status(error_code); network::URLLoaderCompletionStatus status(error_code);
status.resolve_error_info = resolve_error_info_;
status.ssl_info = ssl_info_; status.ssl_info = ssl_info_;
url_loader->SimulateErrorWithStatus(status); url_loader->SimulateErrorWithStatus(status);
...@@ -916,6 +917,11 @@ void NavigationSimulatorImpl::SetAutoAdvance(bool auto_advance) { ...@@ -916,6 +917,11 @@ void NavigationSimulatorImpl::SetAutoAdvance(bool auto_advance) {
auto_advance_ = auto_advance; auto_advance_ = auto_advance;
} }
void NavigationSimulatorImpl::SetResolveErrorInfo(
const net::ResolveErrorInfo& resolve_error_info) {
resolve_error_info_ = resolve_error_info;
}
void NavigationSimulatorImpl::SetSSLInfo(const net::SSLInfo& ssl_info) { void NavigationSimulatorImpl::SetSSLInfo(const net::SSLInfo& ssl_info) {
ssl_info_ = ssl_info; ssl_info_ = ssl_info;
} }
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_receiver.h"
#include "net/base/host_port_pair.h" #include "net/base/host_port_pair.h"
#include "net/base/ip_endpoint.h" #include "net/base/ip_endpoint.h"
#include "net/dns/public/resolve_error_info.h"
#include "services/service_manager/public/cpp/interface_provider.h" #include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/public/mojom/referrer.mojom-forward.h" #include "third_party/blink/public/mojom/referrer.mojom-forward.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -95,6 +96,8 @@ class NavigationSimulatorImpl : public NavigationSimulator, ...@@ -95,6 +96,8 @@ class NavigationSimulatorImpl : public NavigationSimulator,
override; override;
void SetContentsMimeType(const std::string& contents_mime_type) override; void SetContentsMimeType(const std::string& contents_mime_type) override;
void SetAutoAdvance(bool auto_advance) override; void SetAutoAdvance(bool auto_advance) override;
void SetResolveErrorInfo(
const net::ResolveErrorInfo& resolve_error_info) override;
void SetSSLInfo(const net::SSLInfo& ssl_info) override; void SetSSLInfo(const net::SSLInfo& ssl_info) override;
NavigationThrottle::ThrottleCheckResult GetLastThrottleCheckResult() override; NavigationThrottle::ThrottleCheckResult GetLastThrottleCheckResult() override;
...@@ -295,6 +298,7 @@ class NavigationSimulatorImpl : public NavigationSimulator, ...@@ -295,6 +298,7 @@ class NavigationSimulatorImpl : public NavigationSimulator,
network::mojom::CSPDisposition::CHECK; network::mojom::CSPDisposition::CHECK;
net::HttpResponseInfo::ConnectionInfo http_connection_info_ = net::HttpResponseInfo::ConnectionInfo http_connection_info_ =
net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN; net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN;
net::ResolveErrorInfo resolve_error_info_ = net::ResolveErrorInfo(net::OK);
base::Optional<net::SSLInfo> ssl_info_; base::Optional<net::SSLInfo> ssl_info_;
base::Optional<PageState> page_state_; base::Optional<PageState> page_state_;
base::Optional<url::Origin> origin_; base::Optional<url::Origin> origin_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment