blob: c839755e7e6fb6515cea469e739e5b777d11d4df [file] [log] [blame]
// Copyright 2019 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 "weblayer/browser/ssl_blocking_page.h"
#include <memory>
#include <utility>
#include "components/security_interstitials/content/security_interstitial_controller_client.h"
#include "components/security_interstitials/core/metrics_helper.h"
#include "components/security_interstitials/core/ssl_error_options_mask.h"
#include "components/security_interstitials/core/ssl_error_ui.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/interstitial_page.h"
#include "content/public/browser/interstitial_page_delegate.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/ssl_host_state_delegate.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/web_contents.h"
#include "net/base/net_errors.h"
namespace weblayer {
namespace {
// A stripped-down version of the class by the same name in
// //chrome/browser/ssl, which provides basic functionality for interacting with
// the SSL interstitial.
class SSLErrorControllerClient
: public security_interstitials::SecurityInterstitialControllerClient {
public:
SSLErrorControllerClient(
content::WebContents* web_contents,
int cert_error,
const net::SSLInfo& ssl_info,
const GURL& request_url,
std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper)
: security_interstitials::SecurityInterstitialControllerClient(
web_contents,
std::move(metrics_helper),
nullptr /*prefs*/,
"en_US",
GURL("chrome://newtab")),
cert_error_(cert_error),
ssl_info_(ssl_info),
request_url_(request_url) {}
~SSLErrorControllerClient() override = default;
void GoBack() override {
SecurityInterstitialControllerClient::GoBackAfterNavigationCommitted();
}
void Proceed() override {
web_contents_->GetBrowserContext()->GetSSLHostStateDelegate()->AllowCert(
request_url_.host(), *ssl_info_.cert.get(), cert_error_);
Reload();
}
void OpenUrlInNewForegroundTab(const GURL& url) override {
// For now WebLayer doesn't support multiple tabs, so just open the Learn
// More link in the current tab.
OpenUrlInCurrentTab(url);
}
private:
const int cert_error_;
const net::SSLInfo ssl_info_;
const GURL request_url_;
DISALLOW_COPY_AND_ASSIGN(SSLErrorControllerClient);
};
} // namespace
// static
const content::InterstitialPageDelegate::TypeID
SSLBlockingPage::kTypeForTesting = &SSLBlockingPage::kTypeForTesting;
// static
SSLBlockingPage* SSLBlockingPage::Create(
content::WebContents* web_contents,
int cert_error,
const net::SSLInfo& ssl_info,
const GURL& request_url,
int options_mask,
const base::Time& time_triggered,
const base::Callback<void(content::CertificateRequestResultType)>&
callback) {
bool overridable = IsOverridable(options_mask);
security_interstitials::MetricsHelper::ReportDetails report_details;
report_details.metric_prefix =
overridable ? "ssl_overridable" : "ssl_nonoverridable";
auto metrics_helper = std::make_unique<security_interstitials::MetricsHelper>(
request_url, report_details, /*history_service=*/nullptr);
return new SSLBlockingPage(web_contents, cert_error, ssl_info, request_url,
options_mask, time_triggered, overridable,
std::move(metrics_helper), callback);
}
bool SSLBlockingPage::ShouldCreateNewNavigation() const {
return true;
}
content::InterstitialPageDelegate::TypeID SSLBlockingPage::GetTypeForTesting() {
return SSLBlockingPage::kTypeForTesting;
}
SSLBlockingPage::~SSLBlockingPage() = default;
void SSLBlockingPage::PopulateInterstitialStrings(
base::DictionaryValue* load_time_data) {
ssl_error_ui_->PopulateStringsForHTML(load_time_data);
}
// Note that we always create a navigation entry with SSL errors.
// No error happening loading a sub-resource triggers an interstitial so far.
SSLBlockingPage::SSLBlockingPage(
content::WebContents* web_contents,
int cert_error,
const net::SSLInfo& ssl_info,
const GURL& request_url,
int options_mask,
const base::Time& time_triggered,
bool overridable,
std::unique_ptr<security_interstitials::MetricsHelper> metrics_helper,
const base::Callback<void(content::CertificateRequestResultType)>& callback)
: security_interstitials::SecurityInterstitialPage(
web_contents,
request_url,
std::make_unique<SSLErrorControllerClient>(
web_contents,
cert_error,
ssl_info,
request_url,
std::move(metrics_helper))),
ssl_info_(ssl_info),
ssl_error_ui_(std::make_unique<security_interstitials::SSLErrorUI>(
request_url,
cert_error,
ssl_info,
options_mask,
time_triggered,
/*support_url=*/GURL(),
controller())) {
DCHECK(callback.is_null());
}
void SSLBlockingPage::OverrideEntry(content::NavigationEntry* entry) {
entry->GetSSL() = content::SSLStatus(ssl_info_);
}
// This handles the commands sent from the interstitial JavaScript.
void SSLBlockingPage::CommandReceived(const std::string& command) {
// content::WaitForRenderFrameReady sends this message when the page
// load completes. Ignore it.
if (command == "\"pageLoadComplete\"")
return;
int cmd = 0;
bool retval = base::StringToInt(command, &cmd);
DCHECK(retval);
ssl_error_ui_->HandleCommand(
static_cast<security_interstitials::SecurityInterstitialCommand>(cmd));
}
void SSLBlockingPage::OnInterstitialClosing() {
// TODO(blundell): Does this need to track metrics analogously to //chrome's
// SSLBlockingPageBase::OnInterstitialClosing()?
}
// static
bool SSLBlockingPage::IsOverridable(int options_mask) {
const bool is_overridable =
(options_mask &
security_interstitials::SSLErrorOptionsMask::SOFT_OVERRIDE_ENABLED) &&
!(options_mask &
security_interstitials::SSLErrorOptionsMask::STRICT_ENFORCEMENT) &&
!(options_mask &
security_interstitials::SSLErrorOptionsMask::HARD_OVERRIDE_DISABLED);
return is_overridable;
}
} // namespace weblayer