1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ssl/ssl_error_handler.h"
7 #include "base/metrics/field_trial.h"
8 #include "base/metrics/histogram.h"
9 #include "base/time/time.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ssl/ssl_blocking_page.h"
12 #include "content/public/browser/notification_service.h"
13 #include "content/public/browser/notification_source.h"
14 #include "content/public/browser/web_contents.h"
16 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
17 #include "chrome/browser/captive_portal/captive_portal_service.h"
18 #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
19 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
20 #include "chrome/browser/ssl/captive_portal_blocking_page.h"
25 // The type of the delay before displaying the SSL interstitial. This can be
27 SSLErrorHandler::InterstitialDelayType g_interstitial_delay_type
=
28 SSLErrorHandler::NORMAL
;
30 // Callback to call when the interstitial timer is started. Used for testing.
31 SSLErrorHandler::TimerStartedCallback
* g_timer_started_callback
= nullptr;
34 enum SSLErrorHandlerEvent
{
36 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE
,
37 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE
,
38 SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE
,
39 SHOW_SSL_INTERSTITIAL_OVERRIDABLE
,
40 SSL_ERROR_HANDLER_EVENT_COUNT
43 void RecordUMA(SSLErrorHandlerEvent event
) {
44 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_handler",
46 SSL_ERROR_HANDLER_EVENT_COUNT
);
49 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
50 // The delay before displaying the SSL interstitial for cert errors.
51 // - If a "captive portal detected" result arrives in this many seconds,
52 // a captive portal interstitial is displayed.
53 // - Otherwise, an SSL interstitial is displayed.
54 const int kDefaultInterstitialDisplayDelayInSeconds
= 2;
56 base::TimeDelta
GetInterstitialDisplayDelay(
57 SSLErrorHandler::InterstitialDelayType delay
) {
59 case SSLErrorHandler::LONG
:
60 return base::TimeDelta::FromHours(1);
62 case SSLErrorHandler::NONE
:
63 return base::TimeDelta();
65 case SSLErrorHandler::NORMAL
:
66 return base::TimeDelta::FromSeconds(
67 kDefaultInterstitialDisplayDelayInSeconds
);
72 return base::TimeDelta();
75 bool IsCaptivePortalInterstitialEnabled() {
76 return base::FieldTrialList::FindFullName("CaptivePortalInterstitial") ==
83 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SSLErrorHandler
);
85 void SSLErrorHandler::HandleSSLError(
86 content::WebContents
* web_contents
,
88 const net::SSLInfo
& ssl_info
,
89 const GURL
& request_url
,
91 const base::Callback
<void(bool)>& callback
) {
92 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
93 CaptivePortalTabHelper
* captive_portal_tab_helper
=
94 CaptivePortalTabHelper::FromWebContents(web_contents
);
95 if (captive_portal_tab_helper
) {
96 captive_portal_tab_helper
->OnSSLCertError(ssl_info
);
99 DCHECK(!FromWebContents(web_contents
));
100 web_contents
->SetUserData(UserDataKey(),
101 new SSLErrorHandler(web_contents
, cert_error
,
102 ssl_info
, request_url
,
103 options_mask
, callback
));
105 SSLErrorHandler
* error_handler
=
106 SSLErrorHandler::FromWebContents(web_contents
);
107 error_handler
->StartHandlingError();
111 void SSLErrorHandler::SetInterstitialDelayTypeForTest(
112 SSLErrorHandler::InterstitialDelayType delay
) {
113 g_interstitial_delay_type
= delay
;
117 void SSLErrorHandler::SetInterstitialTimerStartedCallbackForTest(
118 TimerStartedCallback
* callback
) {
119 DCHECK(!callback
|| !callback
->is_null());
120 g_timer_started_callback
= callback
;
123 SSLErrorHandler::SSLErrorHandler(content::WebContents
* web_contents
,
125 const net::SSLInfo
& ssl_info
,
126 const GURL
& request_url
,
128 const base::Callback
<void(bool)>& callback
)
129 : web_contents_(web_contents
),
130 cert_error_(cert_error
),
132 request_url_(request_url
),
133 options_mask_(options_mask
),
134 callback_(callback
) {
135 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
136 Profile
* profile
= Profile::FromBrowserContext(
137 web_contents
->GetBrowserContext());
139 chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT
,
140 content::Source
<Profile
>(profile
));
144 SSLErrorHandler::~SSLErrorHandler() {
147 void SSLErrorHandler::StartHandlingError() {
148 RecordUMA(HANDLE_ALL
);
150 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
151 if (IsCaptivePortalInterstitialEnabled()) {
152 CheckForCaptivePortal();
153 timer_
.Start(FROM_HERE
,
154 GetInterstitialDisplayDelay(g_interstitial_delay_type
),
155 this, &SSLErrorHandler::OnTimerExpired
);
156 if (g_timer_started_callback
)
157 g_timer_started_callback
->Run(web_contents_
);
161 // Display an SSL interstitial.
162 ShowSSLInterstitial();
165 void SSLErrorHandler::OnTimerExpired() {
166 ShowSSLInterstitial();
169 void SSLErrorHandler::CheckForCaptivePortal() {
170 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
171 Profile
* profile
= Profile::FromBrowserContext(
172 web_contents_
->GetBrowserContext());
173 CaptivePortalService
* captive_portal_service
=
174 CaptivePortalServiceFactory::GetForProfile(profile
);
175 captive_portal_service
->DetectCaptivePortal();
181 void SSLErrorHandler::ShowCaptivePortalInterstitial(const GURL
& landing_url
) {
182 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
183 // Show captive portal blocking page. The interstitial owns the blocking page.
184 RecordUMA(SSLBlockingPage::IsOptionsOverridable(options_mask_
) ?
185 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_OVERRIDABLE
:
186 SHOW_CAPTIVE_PORTAL_INTERSTITIAL_NONOVERRIDABLE
);
187 (new CaptivePortalBlockingPage(web_contents_
, request_url_
, landing_url
,
189 // Once an interstitial is displayed, no need to keep the handler around.
190 // This is the equivalent of "delete this".
191 web_contents_
->RemoveUserData(UserDataKey());
197 void SSLErrorHandler::ShowSSLInterstitial() {
198 // Show SSL blocking page. The interstitial owns the blocking page.
199 RecordUMA(SSLBlockingPage::IsOptionsOverridable(options_mask_
) ?
200 SHOW_SSL_INTERSTITIAL_OVERRIDABLE
:
201 SHOW_SSL_INTERSTITIAL_NONOVERRIDABLE
);
202 (new SSLBlockingPage(web_contents_
, cert_error_
, ssl_info_
, request_url_
,
203 options_mask_
, base::Time::NowFromSystemTime(),
205 // Once an interstitial is displayed, no need to keep the handler around.
206 // This is the equivalent of "delete this".
207 web_contents_
->RemoveUserData(UserDataKey());
210 void SSLErrorHandler::Observe(
212 const content::NotificationSource
& source
,
213 const content::NotificationDetails
& details
) {
214 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
215 if (type
== chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT
) {
217 CaptivePortalService::Results
* results
=
218 content::Details
<CaptivePortalService::Results
>(details
).ptr();
219 if (results
->result
== captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL
)
220 ShowCaptivePortalInterstitial(results
->landing_url
);
222 ShowSSLInterstitial();