Chromium Code Reviews| Index: ios/chrome/browser/ssl/ios_ssl_blocking_page.cc |
| diff --git a/ios/chrome/browser/ssl/ios_ssl_blocking_page.cc b/ios/chrome/browser/ssl/ios_ssl_blocking_page.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..304af8d7ea0b80d56183bd9a27bb7f77c53a6bee |
| --- /dev/null |
| +++ b/ios/chrome/browser/ssl/ios_ssl_blocking_page.cc |
| @@ -0,0 +1,207 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "ios/chrome/browser/ssl/ios_ssl_blocking_page.h" |
| + |
| +#include <utility> |
| + |
| +#include "base/metrics/histogram.h" |
| +#include "base/prefs/pref_service.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "components/security_interstitials/core/metrics_helper.h" |
| +#include "components/security_interstitials/core/ssl_error_ui.h" |
| +#include "ios/chrome/browser/interstitials/ios_chrome_controller_client.h" |
| +#include "ios/chrome/browser/interstitials/ios_chrome_metrics_helper.h" |
| +#include "ios/chrome/browser/pref_names.h" |
| +#include "ios/chrome/grit/ios_strings.h" |
| +#include "ios/public/provider/chrome/browser/browser_constants.h" |
| +#include "ios/public/provider/chrome/browser/browser_state/chrome_browser_state.h" |
| +#include "ios/web/public/web_state/web_state.h" |
| +#include "net/base/net_errors.h" |
| +#include "ui/base/l10n/l10n_util.h" |
| +#include "url/gurl.h" |
| + |
| +using security_interstitials::SSLErrorUI; |
| + |
| +namespace { |
| +// Events for UMA. Do not reorder or change! |
| +enum SSLExpirationAndDecision { |
| + EXPIRED_AND_PROCEED, |
| + EXPIRED_AND_DO_NOT_PROCEED, |
| + NOT_EXPIRED_AND_PROCEED, |
| + NOT_EXPIRED_AND_DO_NOT_PROCEED, |
| + END_OF_SSL_EXPIRATION_AND_DECISION, |
| +}; |
| + |
| +// Rappor prefix, which is used for both overridable and non-overridable |
| +// interstitials so we don't leak the "overridable" bit. |
| +const char kSSLRapporPrefix[] = "ssl2"; |
|
droger
2016/01/06 17:28:03
Probably not for this CL, but it seems that it wou
sdefresne
2016/01/06 17:31:02
https://code.google.com/p/chromium/issues/detail?i
|
| + |
| +void RecordSSLExpirationPageEventState(bool expired_but_previously_allowed, |
| + bool proceed, |
| + bool overridable) { |
| + SSLExpirationAndDecision event; |
| + if (expired_but_previously_allowed && proceed) |
| + event = EXPIRED_AND_PROCEED; |
| + else if (expired_but_previously_allowed && !proceed) |
| + event = EXPIRED_AND_DO_NOT_PROCEED; |
| + else if (!expired_but_previously_allowed && proceed) |
| + event = NOT_EXPIRED_AND_PROCEED; |
| + else |
| + event = NOT_EXPIRED_AND_DO_NOT_PROCEED; |
| + |
| + if (overridable) { |
| + UMA_HISTOGRAM_ENUMERATION( |
| + "interstitial.ssl.expiration_and_decision.overridable", event, |
| + END_OF_SSL_EXPIRATION_AND_DECISION); |
| + } else { |
| + UMA_HISTOGRAM_ENUMERATION( |
| + "interstitial.ssl.expiration_and_decision.nonoverridable", event, |
| + END_OF_SSL_EXPIRATION_AND_DECISION); |
| + } |
| +} |
| +} // namespace |
| + |
| +// Note that we always create a navigation entry with SSL errors. |
| +// No error happening loading a sub-resource triggers an interstitial so far. |
| +IOSSSLBlockingPage::IOSSSLBlockingPage( |
| + web::WebState* web_state, |
| + int cert_error, |
| + const net::SSLInfo& ssl_info, |
| + const GURL& request_url, |
| + int options_mask, |
| + const base::Time& time_triggered, |
| + const base::Callback<void(bool)>& callback) |
| + : IOSSecurityInterstitialPage(web_state, request_url), |
| + callback_(callback), |
| + ssl_info_(ssl_info), |
| + overridable_(IsOverridable(options_mask)), |
| + expired_but_previously_allowed_( |
| + (options_mask & SSLErrorUI::EXPIRED_BUT_PREVIOUSLY_ALLOWED) != 0), |
| + controller_(new IOSChromeControllerClient(web_state)) { |
| + // Get the language and override prefs for the SSLErrorUI. |
| + std::string languages; |
| + ios::ChromeBrowserState* browser_state = |
| + ios::ChromeBrowserState::FromBrowserState(web_state->GetBrowserState()); |
| + if (browser_state) { |
| + languages = browser_state->GetPrefs()->GetString(prefs::kAcceptLanguages); |
| + } |
| + if (overridable_) |
| + options_mask |= SSLErrorUI::SOFT_OVERRIDE_ENABLED; |
| + else |
| + options_mask &= ~SSLErrorUI::SOFT_OVERRIDE_ENABLED; |
| + |
| + // Set up the metrics helper for the SSLErrorUI. |
| + security_interstitials::MetricsHelper::ReportDetails reporting_info; |
| + reporting_info.metric_prefix = |
| + overridable_ ? "ssl_overridable" : "ssl_nonoverridable"; |
| + reporting_info.rappor_prefix = kSSLRapporPrefix; |
| + reporting_info.rappor_report_type = rappor::UMA_RAPPOR_TYPE; |
| + IOSChromeMetricsHelper* ios_chrome_metrics_helper = |
| + new IOSChromeMetricsHelper(web_state, request_url, reporting_info); |
| + controller_->set_metrics_helper(make_scoped_ptr(ios_chrome_metrics_helper)); |
| + |
| + ssl_error_ui_.reset(new SSLErrorUI(request_url, cert_error, ssl_info, |
| + options_mask, time_triggered, languages, |
| + controller_.get())); |
| + |
| + // Creating an interstitial without showing (e.g. from chrome://interstitials) |
| + // it leaks memory, so don't create it here. |
| +} |
| + |
| +bool IOSSSLBlockingPage::ShouldCreateNewNavigation() const { |
| + return true; |
| +} |
| + |
| +IOSSSLBlockingPage::~IOSSSLBlockingPage() { |
| + if (!callback_.is_null()) { |
| + // The page is closed without the user having chosen what to do, default to |
| + // deny. |
| + RecordSSLExpirationPageEventState(expired_but_previously_allowed_, false, |
| + overridable_); |
| + NotifyDenyCertificate(); |
| + } |
| +} |
| + |
| +void IOSSSLBlockingPage::AfterShow() { |
| + controller_->SetWebInterstitial(web_interstitial()); |
| +} |
| + |
| +void IOSSSLBlockingPage::PopulateInterstitialStrings( |
| + base::DictionaryValue* load_time_data) const { |
| + ssl_error_ui_->PopulateStringsForHTML(load_time_data); |
| + // Spoofing attempts have a custom message on iOS. |
| + // This code will no longer be necessary once UIWebView is gone. |
| + if (ssl_info_.cert->subject().GetDisplayName() == ios::kSpoofingAttemptFlag) { |
| + load_time_data->SetString( |
| + "errorCode", base::string16(base::ASCIIToUTF16("Unverified URL"))); |
| + load_time_data->SetString( |
| + "tabTitle", l10n_util::GetStringUTF16( |
| + IDS_IOS_INTERSTITIAL_HEADING_SPOOFING_ATTEMPT_ERROR)); |
| + load_time_data->SetString( |
| + "heading", l10n_util::GetStringUTF16( |
| + IDS_IOS_INTERSTITIAL_HEADING_SPOOFING_ATTEMPT_ERROR)); |
| + load_time_data->SetString( |
| + "primaryParagraph", |
| + l10n_util::GetStringUTF16( |
| + IDS_IOS_INTERSTITIAL_SUMMARY_SPOOFING_ATTEMPT_ERROR)); |
| + load_time_data->SetString( |
| + "explanationParagraph", |
| + l10n_util::GetStringUTF16( |
| + IDS_IOS_INTERSTITIAL_DETAILS_SPOOFING_ATTEMPT_ERROR)); |
| + load_time_data->SetString("finalParagraph", base::string16()); |
| + } |
| +} |
| + |
| +// This handles the commands sent from the interstitial JavaScript. |
| +void IOSSSLBlockingPage::CommandReceived(const std::string& command) { |
| + if (command == "\"pageLoadComplete\"") { |
| + // content::WaitForRenderFrameReady sends this message when the page |
| + // load completes. Ignore it. |
| + return; |
| + } |
| + |
| + int cmd = 0; |
| + bool retval = base::StringToInt(command, &cmd); |
| + DCHECK(retval); |
| + ssl_error_ui_->HandleCommand( |
| + static_cast<security_interstitials::SecurityInterstitialCommands>(cmd)); |
| +} |
| + |
| +void IOSSSLBlockingPage::OnProceed() { |
| + RecordSSLExpirationPageEventState(expired_but_previously_allowed_, true, |
| + overridable_); |
| + |
| + // Accepting the certificate resumes the loading of the page. |
| + DCHECK(!callback_.is_null()); |
| + callback_.Run(true); |
| + callback_.Reset(); |
| +} |
| + |
| +void IOSSSLBlockingPage::OnDontProceed() { |
| + RecordSSLExpirationPageEventState(expired_but_previously_allowed_, false, |
| + overridable_); |
| + |
| + NotifyDenyCertificate(); |
| +} |
| + |
| +void IOSSSLBlockingPage::NotifyDenyCertificate() { |
| + // It's possible that callback_ may not exist if the user clicks "Proceed" |
| + // followed by pressing the back button before the interstitial is hidden. |
| + // In that case the certificate will still be treated as allowed. |
| + if (callback_.is_null()) |
| + return; |
| + |
| + callback_.Run(false); |
| + callback_.Reset(); |
| +} |
| + |
| +// static |
| +bool IOSSSLBlockingPage::IsOverridable(int options_mask) { |
| + const bool is_overridable = |
| + (options_mask & SSLErrorUI::SOFT_OVERRIDE_ENABLED) && |
| + !(options_mask & SSLErrorUI::STRICT_ENFORCEMENT); |
| + return is_overridable; |
| +} |