From cd7e8a055816f1bfc681c18a2e959ef3e414f265 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Tue, 23 Feb 2016 14:29:18 -0500 Subject: [PATCH] Allow clients to clear certificate exceptions and close connections (issue #1793) --- include/capi/cef_request_context_capi.h | 23 ++++ include/cef_request_context.h | 23 ++++ libcef/browser/request_context_impl.cc | 57 +++++++++ libcef/browser/request_context_impl.h | 9 ++ libcef_dll/cpptoc/request_context_cpptoc.cc | 31 +++++ libcef_dll/ctocpp/request_context_ctocpp.cc | 30 +++++ libcef_dll/ctocpp/request_context_ctocpp.h | 3 + tests/unittests/request_context_unittest.cc | 132 ++++++++++++++++++++ 8 files changed, 308 insertions(+) diff --git a/include/capi/cef_request_context_capi.h b/include/capi/cef_request_context_capi.h index 1f04e206b..e2f1247f9 100644 --- a/include/capi/cef_request_context_capi.h +++ b/include/capi/cef_request_context_capi.h @@ -38,6 +38,7 @@ #define CEF_INCLUDE_CAPI_CEF_REQUEST_CONTEXT_CAPI_H_ #pragma once +#include "include/capi/cef_callback_capi.h" #include "include/capi/cef_cookie_capi.h" #include "include/capi/cef_request_context_handler_capi.h" #include "include/capi/cef_values_capi.h" @@ -199,6 +200,28 @@ typedef struct _cef_request_context_t { int (CEF_CALLBACK *set_preference)(struct _cef_request_context_t* self, const cef_string_t* name, struct _cef_value_t* value, cef_string_t* error); + + /// + // Clears all certificate exceptions that were added as part of handling + // cef_request_tHandler::on_certificate_error(). If you call this it is + // recommended that you also call close_all_connections() or you risk not + // being prompted again for server certificates if you reconnect quickly. If + // |callback| is non-NULL it will be executed on the UI thread after + // completion. + /// + void (CEF_CALLBACK *clear_certificate_exceptions)( + struct _cef_request_context_t* self, + struct _cef_completion_callback_t* callback); + + /// + // Clears all active and idle connections that Chromium currently has. This is + // only recommended if you have released all other CEF objects but don't yet + // want to call cef_shutdown(). If |callback| is non-NULL it will be executed + // on the UI thread after completion. + /// + void (CEF_CALLBACK *close_all_connections)( + struct _cef_request_context_t* self, + struct _cef_completion_callback_t* callback); } cef_request_context_t; diff --git a/include/cef_request_context.h b/include/cef_request_context.h index e2ccf2627..f448f74be 100644 --- a/include/cef_request_context.h +++ b/include/cef_request_context.h @@ -38,6 +38,7 @@ #define CEF_INCLUDE_CEF_REQUEST_CONTEXT_H_ #pragma once +#include "include/cef_callback.h" #include "include/cef_cookie.h" #include "include/cef_request_context_handler.h" #include "include/cef_values.h" @@ -217,6 +218,28 @@ class CefRequestContext : public virtual CefBase { virtual bool SetPreference(const CefString& name, CefRefPtr value, CefString& error) =0; + + /// + // Clears all certificate exceptions that were added as part of handling + // CefRequestHandler::OnCertificateError(). If you call this it is + // recommended that you also call CloseAllConnections() or you risk not + // being prompted again for server certificates if you reconnect quickly. + // If |callback| is non-NULL it will be executed on the UI thread after + // completion. + /// + /*--cef(optional_param=callback)--*/ + virtual void ClearCertificateExceptions( + CefRefPtr callback) =0; + + /// + // Clears all active and idle connections that Chromium currently has. + // This is only recommended if you have released all other CEF objects but + // don't yet want to call CefShutdown(). If |callback| is non-NULL it will be + // executed on the UI thread after completion. + /// + /*--cef(optional_param=callback)--*/ + virtual void CloseAllConnections( + CefRefPtr callback) =0; }; #endif // CEF_INCLUDE_CEF_REQUEST_CONTEXT_H_ diff --git a/libcef/browser/request_context_impl.cc b/libcef/browser/request_context_impl.cc index 8eb63c6b6..9e6221663 100644 --- a/libcef/browser/request_context_impl.cc +++ b/libcef/browser/request_context_impl.cc @@ -16,6 +16,9 @@ #include "base/prefs/pref_service.h" #include "base/strings/stringprintf.h" #include "content/public/browser/plugin_service.h" +#include "content/public/browser/ssl_host_state_delegate.h" +#include "net/http/http_cache.h" +#include "net/http/http_transaction_factory.h" using content::BrowserThread; @@ -444,6 +447,22 @@ bool CefRequestContextImpl::SetPreference(const CefString& name, return true; } +void CefRequestContextImpl::ClearCertificateExceptions( + CefRefPtr callback) { + GetBrowserContext( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), + base::Bind(&CefRequestContextImpl::ClearCertificateExceptionsInternal, + this, callback)); +} + +void CefRequestContextImpl::CloseAllConnections( + CefRefPtr callback) { + GetRequestContextImpl( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), + base::Bind(&CefRequestContextImpl::CloseAllConnectionsInternal, this, + callback)); +} + CefRequestContextImpl::CefRequestContextImpl( scoped_refptr browser_context) : browser_context_(browser_context), @@ -548,3 +567,41 @@ void CefRequestContextImpl::PurgePluginListCacheInternal( content::PluginService::GetInstance()->PurgePluginListCache( browser_context.get(), false); } + +void CefRequestContextImpl::ClearCertificateExceptionsInternal( + CefRefPtr callback, + scoped_refptr browser_context) { + CEF_REQUIRE_UIT(); + + content::SSLHostStateDelegate* ssl_delegate = + browser_context->GetSSLHostStateDelegate(); + if (ssl_delegate) + ssl_delegate->Clear(); + + if (callback) { + CEF_POST_TASK(CEF_UIT, + base::Bind(&CefCompletionCallback::OnComplete, callback.get())); + } +} + +void CefRequestContextImpl::CloseAllConnectionsInternal( + CefRefPtr callback, + scoped_refptr request_context) { + CEF_REQUIRE_IOT(); + + net::URLRequestContext* url_context = request_context->GetURLRequestContext(); + if (url_context) { + net::HttpTransactionFactory* http_factory = + url_context->http_transaction_factory(); + if (http_factory) { + net::HttpCache* cache = http_factory->GetCache(); + if (cache) + cache->CloseAllConnections(); + } + } + + if (callback) { + CEF_POST_TASK(CEF_UIT, + base::Bind(&CefCompletionCallback::OnComplete, callback.get())); + } +} diff --git a/libcef/browser/request_context_impl.h b/libcef/browser/request_context_impl.h index ac8aa2140..d72beaf10 100644 --- a/libcef/browser/request_context_impl.h +++ b/libcef/browser/request_context_impl.h @@ -69,6 +69,9 @@ class CefRequestContextImpl : public CefRequestContext { bool SetPreference(const CefString& name, CefRefPtr value, CefString& error) override; + void ClearCertificateExceptions( + CefRefPtr callback) override; + void CloseAllConnections(CefRefPtr callback) override; const CefRequestContextSettings& settings() const { return settings_; } @@ -103,6 +106,12 @@ class CefRequestContextImpl : public CefRequestContext { void PurgePluginListCacheInternal( bool reload_pages, scoped_refptr browser_context); + void ClearCertificateExceptionsInternal( + CefRefPtr callback, + scoped_refptr browser_context); + void CloseAllConnectionsInternal( + CefRefPtr callback, + scoped_refptr request_context); scoped_refptr browser_context_; CefRequestContextSettings settings_; diff --git a/libcef_dll/cpptoc/request_context_cpptoc.cc b/libcef_dll/cpptoc/request_context_cpptoc.cc index 7f0c9b53c..62e49a555 100644 --- a/libcef_dll/cpptoc/request_context_cpptoc.cc +++ b/libcef_dll/cpptoc/request_context_cpptoc.cc @@ -350,6 +350,34 @@ int CEF_CALLBACK request_context_set_preference( return _retval; } +void CEF_CALLBACK request_context_clear_certificate_exceptions( + struct _cef_request_context_t* self, cef_completion_callback_t* callback) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Unverified params: callback + + // Execute + CefRequestContextCppToC::Get(self)->ClearCertificateExceptions( + CefCompletionCallbackCToCpp::Wrap(callback)); +} + +void CEF_CALLBACK request_context_close_all_connections( + struct _cef_request_context_t* self, cef_completion_callback_t* callback) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + // Unverified params: callback + + // Execute + CefRequestContextCppToC::Get(self)->CloseAllConnections( + CefCompletionCallbackCToCpp::Wrap(callback)); +} + } // namespace @@ -374,6 +402,9 @@ CefRequestContextCppToC::CefRequestContextCppToC() { GetStruct()->get_all_preferences = request_context_get_all_preferences; GetStruct()->can_set_preference = request_context_can_set_preference; GetStruct()->set_preference = request_context_set_preference; + GetStruct()->clear_certificate_exceptions = + request_context_clear_certificate_exceptions; + GetStruct()->close_all_connections = request_context_close_all_connections; } template<> CefRefPtr CefCppToC callback) { + cef_request_context_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, clear_certificate_exceptions)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Unverified params: callback + + // Execute + _struct->clear_certificate_exceptions(_struct, + CefCompletionCallbackCppToC::Wrap(callback)); +} + +void CefRequestContextCToCpp::CloseAllConnections( + CefRefPtr callback) { + cef_request_context_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, close_all_connections)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Unverified params: callback + + // Execute + _struct->close_all_connections(_struct, + CefCompletionCallbackCppToC::Wrap(callback)); +} + // CONSTRUCTOR - Do not edit by hand. diff --git a/libcef_dll/ctocpp/request_context_ctocpp.h b/libcef_dll/ctocpp/request_context_ctocpp.h index 1f83d55b8..fee86fd57 100644 --- a/libcef_dll/ctocpp/request_context_ctocpp.h +++ b/libcef_dll/ctocpp/request_context_ctocpp.h @@ -52,6 +52,9 @@ class CefRequestContextCToCpp bool CanSetPreference(const CefString& name) OVERRIDE; bool SetPreference(const CefString& name, CefRefPtr value, CefString& error) OVERRIDE; + void ClearCertificateExceptions( + CefRefPtr callback) OVERRIDE; + void CloseAllConnections(CefRefPtr callback) OVERRIDE; }; #endif // USING_CEF_SHARED diff --git a/tests/unittests/request_context_unittest.cc b/tests/unittests/request_context_unittest.cc index 513b4979a..d67423729 100644 --- a/tests/unittests/request_context_unittest.cc +++ b/tests/unittests/request_context_unittest.cc @@ -650,3 +650,135 @@ TEST(RequestContextTest, NoReferrerLinkDifferentOrigin) { handler->ExecuteTest(); ReleaseAndWaitForDestructor(handler); } + + +namespace { + +class MethodTestHandler : public TestHandler { + public: + enum Method { + METHOD_CLEAR_CERTIFICATE_EXCEPTIONS, + METHOD_CLOSE_ALL_CONNECTIONS, + }; + + class CompletionCallback : public CefCompletionCallback { + public: + explicit CompletionCallback(MethodTestHandler* test_handler) + : test_handler_(test_handler) { + } + + ~CompletionCallback() override { + EXPECT_UI_THREAD(); + + // OnComplete should be executed. + EXPECT_FALSE(test_handler_); + } + + void OnComplete() override { + EXPECT_UI_THREAD(); + + // OnComplete should be executed only one time. + EXPECT_TRUE(test_handler_); + test_handler_->OnCompleteCallback(); + test_handler_ = nullptr; + } + + private: + MethodTestHandler* test_handler_; + + IMPLEMENT_REFCOUNTING(CompletionCallback); + }; + + MethodTestHandler(bool global_context, + Method method) + : global_context_(global_context), + method_(method) { + } + + void RunTest() override { + const char kUrl[] = "http://tests/method.html"; + + AddResource(kUrl, "Method", "text/html"); + + CefRefPtr request_context; + if (!global_context_) { + CefRequestContextSettings settings; + request_context = CefRequestContext::CreateContext(settings, nullptr); + } + + CreateBrowser(kUrl, request_context); + + // Time out the test after a reasonable period of time. + SetTestTimeout(); + } + + void OnLoadEnd(CefRefPtr browser, + CefRefPtr frame, + int httpStatusCode) override { + CefRefPtr context = + browser->GetHost()->GetRequestContext(); + CefRefPtr callback = new CompletionCallback(this); + if (method_ == METHOD_CLEAR_CERTIFICATE_EXCEPTIONS) + context->ClearCertificateExceptions(callback); + else if (method_ == METHOD_CLOSE_ALL_CONNECTIONS) + context->CloseAllConnections(callback); + } + + void OnCompleteCallback() { + EXPECT_UI_THREAD(); + EXPECT_FALSE(got_completion_callback_); + got_completion_callback_.yes(); + DestroyTest(); + } + + private: + void DestroyTest() override { + EXPECT_TRUE(got_completion_callback_); + TestHandler::DestroyTest(); + } + + const bool global_context_; + const Method method_; + + TrackCallback got_completion_callback_; + + IMPLEMENT_REFCOUNTING(MethodTestHandler); +}; + +} // namespace + +// Test CefRequestContext::ClearCertificateExceptions with the global context. +TEST(RequestContextTest, ClearCertificateExceptionsGlobal) { + CefRefPtr handler = + new MethodTestHandler(true, + MethodTestHandler::METHOD_CLEAR_CERTIFICATE_EXCEPTIONS); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + +// Test CefRequestContext::ClearCertificateExceptions with a custom context. +TEST(RequestContextTest, ClearCertificateExceptionsCustom) { + CefRefPtr handler = + new MethodTestHandler(false, + MethodTestHandler::METHOD_CLEAR_CERTIFICATE_EXCEPTIONS); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + +// Test CefRequestContext::CloseAllConnections with the global context. +TEST(RequestContextTest, CloseAllConnectionsGlobal) { + CefRefPtr handler = + new MethodTestHandler(true, + MethodTestHandler::METHOD_CLOSE_ALL_CONNECTIONS); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} + +// Test CefRequestContext::CloseAllConnections with a custom context. +TEST(RequestContextTest, CloseAllConnectionsCustom) { + CefRefPtr handler = + new MethodTestHandler(false, + MethodTestHandler::METHOD_CLOSE_ALL_CONNECTIONS); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); +} \ No newline at end of file