| // 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 |