ssl_blocking_page.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
1// Copyright (c) 2012 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.
4
5#include "chrome/browser/ssl/ssl_blocking_page.h"
6
7#include "base/build_time.h"
8#include "base/command_line.h"
9#include "base/i18n/rtl.h"
10#include "base/i18n/time_formatting.h"
11#include "base/metrics/field_trial.h"
12#include "base/metrics/histogram.h"
13#include "base/process/launch.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/strings/string_piece.h"
16#include "base/strings/stringprintf.h"
17#include "base/strings/utf_string_conversions.h"
18#include "base/time/time.h"
19#include "base/values.h"
20#include "chrome/browser/chrome_notification_types.h"
21#include "chrome/browser/history/history_service_factory.h"
22#include "chrome/browser/profiles/profile.h"
23#include "chrome/browser/renderer_preferences_util.h"
24#include "chrome/browser/ssl/ssl_error_classification.h"
25#include "chrome/browser/ssl/ssl_error_info.h"
26#include "chrome/common/chrome_switches.h"
27#include "chrome/grit/chromium_strings.h"
28#include "chrome/grit/generated_resources.h"
29#include "content/public/browser/cert_store.h"
30#include "content/public/browser/interstitial_page.h"
31#include "content/public/browser/navigation_controller.h"
32#include "content/public/browser/navigation_entry.h"
33#include "content/public/browser/notification_service.h"
34#include "content/public/browser/notification_types.h"
35#include "content/public/browser/render_process_host.h"
36#include "content/public/browser/render_view_host.h"
37#include "content/public/browser/web_contents.h"
38#include "content/public/common/ssl_status.h"
39#include "grit/app_locale_settings.h"
40#include "grit/browser_resources.h"
41#include "net/base/hash_value.h"
42#include "net/base/net_errors.h"
43#include "net/base/net_util.h"
44#include "ui/base/l10n/l10n_util.h"
45#include "ui/base/resource/resource_bundle.h"
46#include "ui/base/webui/jstemplate_builder.h"
47#include "ui/base/webui/web_ui_util.h"
48
49#if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
50#include "chrome/browser/captive_portal/captive_portal_service.h"
51#include "chrome/browser/captive_portal/captive_portal_service_factory.h"
52#endif
53
54#if defined(ENABLE_EXTENSIONS)
55#include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h"
56#endif
57
58#if defined(OS_WIN)
59#include "base/base_paths_win.h"
60#include "base/path_service.h"
61#include "base/strings/string16.h"
62#include "base/win/windows_version.h"
63#endif
64
65#if defined(OS_CHROMEOS)
66#include "chrome/browser/profiles/profile_manager.h"
67#include "chrome/browser/ui/chrome_pages.h"
68#include "chrome/common/url_constants.h"
69#endif
70
71using base::ASCIIToUTF16;
72using base::TimeTicks;
73using content::InterstitialPage;
74using content::NavigationController;
75using content::NavigationEntry;
76
77#if defined(ENABLE_EXTENSIONS)
78using extensions::ExperienceSamplingEvent;
79#endif
80
81namespace {
82
83// Constants for the Experience Sampling instrumentation.
84#if defined(ENABLE_EXTENSIONS)
85const char kEventNameBase[] = "ssl_interstitial_";
86const char kEventNotOverridable[] = "notoverridable_";
87const char kEventOverridable[] = "overridable_";
88#endif
89
90// Events for UMA. Do not reorder or change!
91enum SSLBlockingPageEvent {
92  SHOW_ALL,
93  SHOW_OVERRIDABLE,
94  PROCEED_OVERRIDABLE,
95  PROCEED_NAME,
96  PROCEED_DATE,
97  PROCEED_AUTHORITY,
98  DONT_PROCEED_OVERRIDABLE,
99  DONT_PROCEED_NAME,
100  DONT_PROCEED_DATE,
101  DONT_PROCEED_AUTHORITY,
102  MORE,
103  SHOW_UNDERSTAND,  // Used by the summer 2013 Finch trial. Deprecated.
104  SHOW_INTERNAL_HOSTNAME,
105  PROCEED_INTERNAL_HOSTNAME,
106  SHOW_NEW_SITE,
107  PROCEED_NEW_SITE,
108  PROCEED_MANUAL_NONOVERRIDABLE,
109  CAPTIVE_PORTAL_DETECTION_ENABLED,
110  CAPTIVE_PORTAL_DETECTION_ENABLED_OVERRIDABLE,
111  CAPTIVE_PORTAL_PROBE_COMPLETED,
112  CAPTIVE_PORTAL_PROBE_COMPLETED_OVERRIDABLE,
113  CAPTIVE_PORTAL_NO_RESPONSE,
114  CAPTIVE_PORTAL_NO_RESPONSE_OVERRIDABLE,
115  CAPTIVE_PORTAL_DETECTED,
116  CAPTIVE_PORTAL_DETECTED_OVERRIDABLE,
117  UNUSED_BLOCKING_PAGE_EVENT,
118};
119
120// Events for UMA. Do not reorder or change!
121enum SSLExpirationAndDecision {
122  EXPIRED_AND_PROCEED,
123  EXPIRED_AND_DO_NOT_PROCEED,
124  NOT_EXPIRED_AND_PROCEED,
125  NOT_EXPIRED_AND_DO_NOT_PROCEED,
126  END_OF_SSL_EXPIRATION_AND_DECISION,
127};
128
129void RecordSSLBlockingPageEventStats(SSLBlockingPageEvent event) {
130  UMA_HISTOGRAM_ENUMERATION("interstitial.ssl",
131                            event,
132                            UNUSED_BLOCKING_PAGE_EVENT);
133}
134
135void RecordSSLExpirationPageEventState(bool expired_but_previously_allowed,
136                                       bool proceed,
137                                       bool overridable) {
138  SSLExpirationAndDecision event;
139  if (expired_but_previously_allowed && proceed)
140    event = EXPIRED_AND_PROCEED;
141  else if (expired_but_previously_allowed && !proceed)
142    event = EXPIRED_AND_DO_NOT_PROCEED;
143  else if (!expired_but_previously_allowed && proceed)
144    event = NOT_EXPIRED_AND_PROCEED;
145  else
146    event = NOT_EXPIRED_AND_DO_NOT_PROCEED;
147
148  if (overridable) {
149    UMA_HISTOGRAM_ENUMERATION(
150        "interstitial.ssl.expiration_and_decision.overridable",
151        event,
152        END_OF_SSL_EXPIRATION_AND_DECISION);
153  } else {
154    UMA_HISTOGRAM_ENUMERATION(
155        "interstitial.ssl.expiration_and_decision.nonoverridable",
156        event,
157        END_OF_SSL_EXPIRATION_AND_DECISION);
158  }
159}
160
161void RecordSSLBlockingPageDetailedStats(bool proceed,
162                                        int cert_error,
163                                        bool overridable,
164                                        bool internal,
165                                        int num_visits,
166                                        bool captive_portal_detection_enabled,
167                                        bool captive_portal_probe_completed,
168                                        bool captive_portal_no_response,
169                                        bool captive_portal_detected,
170                                        bool expired_but_previously_allowed) {
171  UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_type",
172      SSLErrorInfo::NetErrorToErrorType(cert_error), SSLErrorInfo::END_OF_ENUM);
173  RecordSSLExpirationPageEventState(
174      expired_but_previously_allowed, proceed, overridable);
175#if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
176  if (captive_portal_detection_enabled)
177    RecordSSLBlockingPageEventStats(
178        overridable ?
179        CAPTIVE_PORTAL_DETECTION_ENABLED_OVERRIDABLE :
180        CAPTIVE_PORTAL_DETECTION_ENABLED);
181  if (captive_portal_probe_completed)
182    RecordSSLBlockingPageEventStats(
183        overridable ?
184        CAPTIVE_PORTAL_PROBE_COMPLETED_OVERRIDABLE :
185        CAPTIVE_PORTAL_PROBE_COMPLETED);
186  // Log only one of portal detected and no response results.
187  if (captive_portal_detected)
188    RecordSSLBlockingPageEventStats(
189        overridable ?
190        CAPTIVE_PORTAL_DETECTED_OVERRIDABLE :
191        CAPTIVE_PORTAL_DETECTED);
192  else if (captive_portal_no_response)
193    RecordSSLBlockingPageEventStats(
194        overridable ?
195        CAPTIVE_PORTAL_NO_RESPONSE_OVERRIDABLE :
196        CAPTIVE_PORTAL_NO_RESPONSE);
197#endif
198  if (!overridable) {
199    if (proceed) {
200      RecordSSLBlockingPageEventStats(PROCEED_MANUAL_NONOVERRIDABLE);
201    }
202    // Overridable is false if the user didn't have any option except to turn
203    // back. If that's the case, don't record some of the metrics.
204    return;
205  }
206  if (num_visits == 0)
207    RecordSSLBlockingPageEventStats(SHOW_NEW_SITE);
208  if (proceed) {
209    RecordSSLBlockingPageEventStats(PROCEED_OVERRIDABLE);
210    if (internal)
211      RecordSSLBlockingPageEventStats(PROCEED_INTERNAL_HOSTNAME);
212    if (num_visits == 0)
213      RecordSSLBlockingPageEventStats(PROCEED_NEW_SITE);
214  } else if (!proceed) {
215    RecordSSLBlockingPageEventStats(DONT_PROCEED_OVERRIDABLE);
216  }
217  SSLErrorInfo::ErrorType type = SSLErrorInfo::NetErrorToErrorType(cert_error);
218  switch (type) {
219    case SSLErrorInfo::CERT_COMMON_NAME_INVALID: {
220      if (proceed)
221        RecordSSLBlockingPageEventStats(PROCEED_NAME);
222      else
223        RecordSSLBlockingPageEventStats(DONT_PROCEED_NAME);
224      break;
225    }
226    case SSLErrorInfo::CERT_DATE_INVALID: {
227      if (proceed)
228        RecordSSLBlockingPageEventStats(PROCEED_DATE);
229      else
230        RecordSSLBlockingPageEventStats(DONT_PROCEED_DATE);
231      break;
232    }
233    case SSLErrorInfo::CERT_AUTHORITY_INVALID: {
234      if (proceed)
235        RecordSSLBlockingPageEventStats(PROCEED_AUTHORITY);
236      else
237        RecordSSLBlockingPageEventStats(DONT_PROCEED_AUTHORITY);
238      break;
239    }
240    default: {
241      break;
242    }
243  }
244}
245
246void LaunchDateAndTimeSettings() {
247#if defined(OS_CHROMEOS)
248  std::string sub_page = std::string(chrome::kSearchSubPage) + "#" +
249      l10n_util::GetStringUTF8(IDS_OPTIONS_SETTINGS_SECTION_TITLE_DATETIME);
250  chrome::ShowSettingsSubPageForProfile(
251      ProfileManager::GetActiveUserProfile(), sub_page);
252  return;
253#elif defined(OS_ANDROID)
254  CommandLine command(base::FilePath("/system/bin/am"));
255  command.AppendArg("start");
256  command.AppendArg(
257      "'com.android.settings/.Settings$DateTimeSettingsActivity'");
258#elif defined(OS_IOS)
259  // Apparently, iOS really does not have a way to launch the date and time
260  // settings. Weird. TODO(palmer): Do something more graceful than ignoring
261  // the user's click! crbug.com/394993
262  return;
263#elif defined(OS_LINUX)
264  struct ClockCommand {
265    const char* pathname;
266    const char* argument;
267  };
268  static const ClockCommand kClockCommands[] = {
269    // GNOME
270    //
271    // NOTE: On old Ubuntu, naming control panels doesn't work, so it
272    // opens the overview. This will have to be good enough.
273    { "/usr/bin/gnome-control-center", "datetime" },
274    { "/usr/local/bin/gnome-control-center", "datetime" },
275    { "/opt/bin/gnome-control-center", "datetime" },
276    // KDE
277    { "/usr/bin/kcmshell4", "clock" },
278    { "/usr/local/bin/kcmshell4", "clock" },
279    { "/opt/bin/kcmshell4", "clock" },
280  };
281
282  CommandLine command(base::FilePath(""));
283  for (size_t i = 0; i < arraysize(kClockCommands); ++i) {
284    base::FilePath pathname(kClockCommands[i].pathname);
285    if (base::PathExists(pathname)) {
286      command.SetProgram(pathname);
287      command.AppendArg(kClockCommands[i].argument);
288      break;
289    }
290  }
291  if (command.GetProgram().empty()) {
292    // Alas, there is nothing we can do.
293    return;
294  }
295#elif defined(OS_MACOSX)
296  CommandLine command(base::FilePath("/usr/bin/open"));
297  command.AppendArg("/System/Library/PreferencePanes/DateAndTime.prefPane");
298#elif defined(OS_WIN)
299  base::FilePath path;
300  PathService::Get(base::DIR_SYSTEM, &path);
301  static const base::char16 kControlPanelExe[] = L"control.exe";
302  path = path.Append(base::string16(kControlPanelExe));
303  CommandLine command(path);
304  command.AppendArg(std::string("/name"));
305  command.AppendArg(std::string("Microsoft.DateAndTime"));
306#else
307  return;
308#endif
309
310#if !defined(OS_CHROMEOS)
311  base::LaunchOptions options;
312  options.wait = false;
313#if defined(OS_LINUX)
314  options.allow_new_privs = true;
315#endif
316  base::LaunchProcess(command, options, NULL);
317#endif
318}
319
320bool IsErrorDueToBadClock(const base::Time& now, int error) {
321  if (SSLErrorInfo::NetErrorToErrorType(error) !=
322          SSLErrorInfo::CERT_DATE_INVALID) {
323    return false;
324  }
325  return SSLErrorClassification::IsUserClockInThePast(now) ||
326      SSLErrorClassification::IsUserClockInTheFuture(now);
327}
328
329}  // namespace
330
331// Note that we always create a navigation entry with SSL errors.
332// No error happening loading a sub-resource triggers an interstitial so far.
333SSLBlockingPage::SSLBlockingPage(content::WebContents* web_contents,
334                                 int cert_error,
335                                 const net::SSLInfo& ssl_info,
336                                 const GURL& request_url,
337                                 int options_mask,
338                                 const base::Callback<void(bool)>& callback)
339    : callback_(callback),
340      web_contents_(web_contents),
341      cert_error_(cert_error),
342      ssl_info_(ssl_info),
343      request_url_(request_url),
344      overridable_(options_mask & OVERRIDABLE &&
345                   !(options_mask & STRICT_ENFORCEMENT)),
346      strict_enforcement_((options_mask & STRICT_ENFORCEMENT) != 0),
347      interstitial_page_(NULL),
348      internal_(false),
349      num_visits_(-1),
350      captive_portal_detection_enabled_(false),
351      captive_portal_probe_completed_(false),
352      captive_portal_no_response_(false),
353      captive_portal_detected_(false),
354      expired_but_previously_allowed_(
355          (options_mask & EXPIRED_BUT_PREVIOUSLY_ALLOWED) != 0) {
356  Profile* profile = Profile::FromBrowserContext(
357      web_contents->GetBrowserContext());
358  // For UMA stats.
359  if (net::IsHostnameNonUnique(request_url_.HostNoBrackets()))
360    internal_ = true;
361  RecordSSLBlockingPageEventStats(SHOW_ALL);
362  if (overridable_) {
363    RecordSSLBlockingPageEventStats(SHOW_OVERRIDABLE);
364    if (internal_)
365      RecordSSLBlockingPageEventStats(SHOW_INTERNAL_HOSTNAME);
366    HistoryService* history_service = HistoryServiceFactory::GetForProfile(
367        profile, Profile::EXPLICIT_ACCESS);
368    if (history_service) {
369      history_service->GetVisibleVisitCountToHost(
370          request_url_,
371          base::Bind(&SSLBlockingPage::OnGotHistoryCount,
372                     base::Unretained(this)),
373          &request_tracker_);
374    }
375  }
376
377  SSLErrorClassification ssl_error_classification(
378      base::Time::NowFromSystemTime(),
379      request_url_,
380      *ssl_info_.cert.get());
381  ssl_error_classification.RecordUMAStatistics(overridable_, cert_error_);
382
383#if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
384  CaptivePortalService* captive_portal_service =
385      CaptivePortalServiceFactory::GetForProfile(profile);
386  captive_portal_detection_enabled_ = captive_portal_service ->enabled();
387  captive_portal_service ->DetectCaptivePortal();
388  registrar_.Add(this,
389                 chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT,
390                 content::Source<Profile>(profile));
391#endif
392
393#if defined(ENABLE_EXTENSIONS)
394  // ExperienceSampling: Set up new sampling event for this interstitial.
395  std::string event_name(kEventNameBase);
396  if (overridable_ && !strict_enforcement_)
397    event_name.append(kEventOverridable);
398  else
399    event_name.append(kEventNotOverridable);
400  event_name.append(net::ErrorToString(cert_error_));
401  sampling_event_.reset(new ExperienceSamplingEvent(
402      event_name,
403      request_url_,
404      web_contents_->GetLastCommittedURL(),
405      web_contents_->GetBrowserContext()));
406#endif
407
408  // Creating an interstitial without showing (e.g. from chrome://interstitials)
409  // it leaks memory, so don't create it here.
410}
411
412SSLBlockingPage::~SSLBlockingPage() {
413  if (!callback_.is_null()) {
414    RecordSSLBlockingPageDetailedStats(false,
415                                       cert_error_,
416                                       overridable_,
417                                       internal_,
418                                       num_visits_,
419                                       captive_portal_detection_enabled_,
420                                       captive_portal_probe_completed_,
421                                       captive_portal_no_response_,
422                                       captive_portal_detected_,
423                                       expired_but_previously_allowed_);
424    // The page is closed without the user having chosen what to do, default to
425    // deny.
426    NotifyDenyCertificate();
427  }
428}
429
430void SSLBlockingPage::Show() {
431  DCHECK(!interstitial_page_);
432  interstitial_page_ = InterstitialPage::Create(
433      web_contents_, true, request_url_, this);
434  interstitial_page_->Show();
435}
436
437std::string SSLBlockingPage::GetHTMLContents() {
438  base::DictionaryValue load_time_data;
439  base::string16 url(ASCIIToUTF16(request_url_.host()));
440  if (base::i18n::IsRTL())
441    base::i18n::WrapStringWithLTRFormatting(&url);
442  webui::SetFontAndTextDirection(&load_time_data);
443
444  // Shared values for both the overridable and non-overridable versions.
445  load_time_data.SetBoolean("ssl", true);
446  load_time_data.SetBoolean("overridable", overridable_);
447  load_time_data.SetString(
448      "tabTitle", l10n_util::GetStringUTF16(IDS_SSL_V2_TITLE));
449  load_time_data.SetString(
450      "heading", l10n_util::GetStringUTF16(IDS_SSL_V2_HEADING));
451
452  base::Time now = base::Time::NowFromSystemTime();
453  bool bad_clock = IsErrorDueToBadClock(now, cert_error_);
454  if (bad_clock) {
455    load_time_data.SetString("primaryParagraph",
456                             l10n_util::GetStringFUTF16(
457                                 IDS_SSL_CLOCK_ERROR,
458                                 url,
459                                 base::TimeFormatShortDate(now)));
460  } else {
461    load_time_data.SetString(
462        "primaryParagraph",
463        l10n_util::GetStringFUTF16(IDS_SSL_V2_PRIMARY_PARAGRAPH, url));
464  }
465
466  load_time_data.SetString(
467     "openDetails",
468     l10n_util::GetStringUTF16(IDS_SSL_V2_OPEN_DETAILS_BUTTON));
469  load_time_data.SetString(
470     "closeDetails",
471     l10n_util::GetStringUTF16(IDS_SSL_V2_CLOSE_DETAILS_BUTTON));
472  load_time_data.SetString("errorCode", net::ErrorToString(cert_error_));
473
474  if (overridable_) {
475    SSLErrorInfo error_info =
476        SSLErrorInfo::CreateError(
477            SSLErrorInfo::NetErrorToErrorType(cert_error_),
478            ssl_info_.cert.get(),
479            request_url_);
480    if (bad_clock) {
481      load_time_data.SetString("explanationParagraph",
482                               l10n_util::GetStringFUTF16(
483                                   IDS_SSL_CLOCK_ERROR_EXPLANATION, url));
484    } else {
485      load_time_data.SetString("explanationParagraph", error_info.details());
486    }
487    load_time_data.SetString(
488        "primaryButtonText",
489        l10n_util::GetStringUTF16(IDS_SSL_OVERRIDABLE_SAFETY_BUTTON));
490    load_time_data.SetString(
491        "finalParagraph",
492        l10n_util::GetStringFUTF16(IDS_SSL_OVERRIDABLE_PROCEED_PARAGRAPH,
493                                   url));
494  } else {
495    SSLErrorInfo::ErrorType type =
496        SSLErrorInfo::NetErrorToErrorType(cert_error_);
497    if (type == SSLErrorInfo::CERT_INVALID && SSLErrorClassification::
498        IsWindowsVersionSP3OrLower()) {
499      load_time_data.SetString(
500          "explanationParagraph",
501          l10n_util::GetStringFUTF16(
502              IDS_SSL_NONOVERRIDABLE_MORE_INVALID_SP3, url));
503    } else if (bad_clock) {
504      load_time_data.SetString("explanationParagraph",
505                               l10n_util::GetStringFUTF16(
506                                   IDS_SSL_CLOCK_ERROR_EXPLANATION, url));
507    } else {
508      load_time_data.SetString("explanationParagraph",
509                               l10n_util::GetStringFUTF16(
510                                   IDS_SSL_NONOVERRIDABLE_MORE, url));
511    }
512    load_time_data.SetString(
513        "primaryButtonText",
514        l10n_util::GetStringUTF16(IDS_SSL_NONOVERRIDABLE_RELOAD_BUTTON));
515    // Customize the help link depending on the specific error type.
516    // Only mark as HSTS if none of the more specific error types apply, and use
517    // INVALID as a fallback if no other string is appropriate.
518    load_time_data.SetInteger("errorType", type);
519    int help_string = IDS_SSL_NONOVERRIDABLE_INVALID;
520    switch (type) {
521      case SSLErrorInfo::CERT_REVOKED:
522        help_string = IDS_SSL_NONOVERRIDABLE_REVOKED;
523        break;
524      case SSLErrorInfo::CERT_PINNED_KEY_MISSING:
525        help_string = IDS_SSL_NONOVERRIDABLE_PINNED;
526        break;
527      case SSLErrorInfo::CERT_INVALID:
528        help_string = IDS_SSL_NONOVERRIDABLE_INVALID;
529        break;
530      default:
531        if (strict_enforcement_)
532          help_string = IDS_SSL_NONOVERRIDABLE_HSTS;
533    }
534    load_time_data.SetString(
535        "finalParagraph", l10n_util::GetStringFUTF16(help_string, url));
536  }
537
538  base::StringPiece html(
539     ResourceBundle::GetSharedInstance().GetRawDataResource(
540         IRD_SECURITY_INTERSTITIAL_HTML));
541  webui::UseVersion2 version;
542  return webui::GetI18nTemplateHtml(html, &load_time_data);
543}
544
545void SSLBlockingPage::OverrideEntry(NavigationEntry* entry) {
546  int cert_id = content::CertStore::GetInstance()->StoreCert(
547      ssl_info_.cert.get(), web_contents_->GetRenderProcessHost()->GetID());
548  DCHECK(cert_id);
549
550  entry->GetSSL().security_style =
551      content::SECURITY_STYLE_AUTHENTICATION_BROKEN;
552  entry->GetSSL().cert_id = cert_id;
553  entry->GetSSL().cert_status = ssl_info_.cert_status;
554  entry->GetSSL().security_bits = ssl_info_.security_bits;
555}
556
557// This handles the commands sent from the interstitial JavaScript. They are
558// defined in chrome/browser/resources/ssl/ssl_errors_common.js.
559// DO NOT reorder or change this logic without also changing the JavaScript!
560void SSLBlockingPage::CommandReceived(const std::string& command) {
561  int cmd = 0;
562  bool retval = base::StringToInt(command, &cmd);
563  DCHECK(retval);
564  switch (cmd) {
565    case CMD_DONT_PROCEED: {
566      interstitial_page_->DontProceed();
567      break;
568    }
569    case CMD_PROCEED: {
570      interstitial_page_->Proceed();
571      break;
572    }
573    case CMD_MORE: {
574      RecordSSLBlockingPageEventStats(MORE);
575#if defined(ENABLE_EXTENSIONS)
576      if (sampling_event_.get())
577        sampling_event_->set_has_viewed_details(true);
578#endif
579      break;
580    }
581    case CMD_RELOAD: {
582      // The interstitial can't refresh itself.
583      web_contents_->GetController().Reload(true);
584      break;
585    }
586    case CMD_HELP: {
587      content::NavigationController::LoadURLParams help_page_params(GURL(
588          "https://support.google.com/chrome/answer/4454607"));
589#if defined(ENABLE_EXTENSIONS)
590      if (sampling_event_.get())
591        sampling_event_->set_has_viewed_learn_more(true);
592#endif
593      web_contents_->GetController().LoadURLWithParams(help_page_params);
594      break;
595    }
596    case CMD_CLOCK: {
597      LaunchDateAndTimeSettings();
598      break;
599    }
600    default: {
601      NOTREACHED();
602    }
603  }
604}
605
606void SSLBlockingPage::OverrideRendererPrefs(
607      content::RendererPreferences* prefs) {
608  Profile* profile = Profile::FromBrowserContext(
609      web_contents_->GetBrowserContext());
610  renderer_preferences_util::UpdateFromSystemSettings(prefs, profile);
611}
612
613void SSLBlockingPage::OnProceed() {
614  RecordSSLBlockingPageDetailedStats(true,
615                                     cert_error_,
616                                     overridable_,
617                                     internal_,
618                                     num_visits_,
619                                     captive_portal_detection_enabled_,
620                                     captive_portal_probe_completed_,
621                                     captive_portal_no_response_,
622                                     captive_portal_detected_,
623                                     expired_but_previously_allowed_);
624#if defined(ENABLE_EXTENSIONS)
625  // ExperienceSampling: Notify that user decided to proceed.
626  if (sampling_event_.get())
627    sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kProceed);
628#endif
629  // Accepting the certificate resumes the loading of the page.
630  NotifyAllowCertificate();
631}
632
633void SSLBlockingPage::OnDontProceed() {
634  RecordSSLBlockingPageDetailedStats(false,
635                                     cert_error_,
636                                     overridable_,
637                                     internal_,
638                                     num_visits_,
639                                     captive_portal_detection_enabled_,
640                                     captive_portal_probe_completed_,
641                                     captive_portal_no_response_,
642                                     captive_portal_detected_,
643                                     expired_but_previously_allowed_);
644#if defined(ENABLE_EXTENSIONS)
645  // ExperienceSampling: Notify that user decided to not proceed.
646  // This also occurs if the user navigates away or closes the tab.
647  if (sampling_event_.get())
648    sampling_event_->CreateUserDecisionEvent(ExperienceSamplingEvent::kDeny);
649#endif
650  NotifyDenyCertificate();
651}
652
653void SSLBlockingPage::NotifyDenyCertificate() {
654  // It's possible that callback_ may not exist if the user clicks "Proceed"
655  // followed by pressing the back button before the interstitial is hidden.
656  // In that case the certificate will still be treated as allowed.
657  if (callback_.is_null())
658    return;
659
660  callback_.Run(false);
661  callback_.Reset();
662}
663
664void SSLBlockingPage::NotifyAllowCertificate() {
665  DCHECK(!callback_.is_null());
666
667  callback_.Run(true);
668  callback_.Reset();
669}
670
671// static
672void SSLBlockingPage::SetExtraInfo(
673    base::DictionaryValue* strings,
674    const std::vector<base::string16>& extra_info) {
675  DCHECK_LT(extra_info.size(), 5U);  // We allow 5 paragraphs max.
676  const char* keys[5] = {
677      "moreInfo1", "moreInfo2", "moreInfo3", "moreInfo4", "moreInfo5"
678  };
679  int i;
680  for (i = 0; i < static_cast<int>(extra_info.size()); i++) {
681    strings->SetString(keys[i], extra_info[i]);
682  }
683  for (; i < 5; i++) {
684    strings->SetString(keys[i], std::string());
685  }
686}
687
688void SSLBlockingPage::OnGotHistoryCount(bool success,
689                                        int num_visits,
690                                        base::Time first_visit) {
691  num_visits_ = num_visits;
692}
693
694void SSLBlockingPage::Observe(
695    int type,
696    const content::NotificationSource& source,
697    const content::NotificationDetails& details) {
698#if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
699  // When detection is disabled, captive portal service always sends
700  // RESULT_INTERNET_CONNECTED. Ignore any probe results in that case.
701  if (!captive_portal_detection_enabled_)
702    return;
703  if (type == chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT) {
704    captive_portal_probe_completed_ = true;
705    CaptivePortalService::Results* results =
706        content::Details<CaptivePortalService::Results>(
707            details).ptr();
708    // If a captive portal was detected at any point when the interstitial was
709    // displayed, assume that the interstitial was caused by a captive portal.
710    // Example scenario:
711    // 1- Interstitial displayed and captive portal detected, setting the flag.
712    // 2- Captive portal detection automatically opens portal login page.
713    // 3- User logs in on the portal login page.
714    // A notification will be received here for RESULT_INTERNET_CONNECTED. Make
715    // sure we don't clear the captive portal flag, since the interstitial was
716    // potentially caused by the captive portal.
717    captive_portal_detected_ = captive_portal_detected_ ||
718        (results->result == captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL);
719    // Also keep track of non-HTTP portals and error cases.
720    captive_portal_no_response_ = captive_portal_no_response_ ||
721        (results->result == captive_portal::RESULT_NO_RESPONSE);
722  }
723#endif
724}
725