From 0ea96ca4b93a98be61482cf78bd076be532d6a8e Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Fri, 25 Sep 2015 14:59:30 +0300 Subject: [PATCH] Call OnBeforePluginLoad when building the plugin list (issue #1708) --- cef.gyp | 2 + include/capi/cef_request_context_capi.h | 10 ++ .../capi/cef_request_context_handler_capi.h | 20 ++- include/cef_request_context.h | 9 ++ include/cef_request_context_handler.h | 22 +-- libcef/browser/browser_context.cc | 3 +- libcef/browser/content_browser_client.cc | 7 +- libcef/browser/content_browser_client.h | 2 + .../plugins/plugin_info_message_filter.cc | 136 +++++------------- .../plugins/plugin_info_message_filter.h | 7 +- .../browser/plugins/plugin_service_filter.cc | 106 ++++++++++++++ .../browser/plugins/plugin_service_filter.h | 46 ++++++ libcef/browser/request_context_impl.cc | 17 +++ libcef/browser/request_context_impl.h | 8 +- libcef/browser/resource_context.cc | 6 +- libcef/browser/resource_context.h | 7 +- libcef_dll/cpptoc/request_context_cpptoc.cc | 15 ++ .../cpptoc/request_context_handler_cpptoc.cc | 6 +- libcef_dll/ctocpp/request_context_ctocpp.cc | 12 ++ libcef_dll/ctocpp/request_context_ctocpp.h | 1 + .../ctocpp/request_context_handler_ctocpp.cc | 6 +- .../cefclient/browser/root_window_manager.cc | 4 +- tests/unittests/plugin_unittest.cc | 130 +++++++++++++---- 23 files changed, 421 insertions(+), 161 deletions(-) create mode 100644 libcef/browser/plugins/plugin_service_filter.cc create mode 100644 libcef/browser/plugins/plugin_service_filter.h diff --git a/cef.gyp b/cef.gyp index 041cf62af..3c896b2af 100644 --- a/cef.gyp +++ b/cef.gyp @@ -1123,6 +1123,8 @@ 'libcef/browser/permission_manager.h', 'libcef/browser/plugins/plugin_info_message_filter.cc', 'libcef/browser/plugins/plugin_info_message_filter.h', + 'libcef/browser/plugins/plugin_service_filter.cc', + 'libcef/browser/plugins/plugin_service_filter.h', 'libcef/browser/print_settings_impl.cc', 'libcef/browser/print_settings_impl.h', 'libcef/browser/printing/printing_message_filter.cc', diff --git a/include/capi/cef_request_context_capi.h b/include/capi/cef_request_context_capi.h index 5c8d24d39..5cfab73a4 100644 --- a/include/capi/cef_request_context_capi.h +++ b/include/capi/cef_request_context_capi.h @@ -140,6 +140,16 @@ typedef struct _cef_request_context_t { /// int (CEF_CALLBACK *clear_scheme_handler_factories)( struct _cef_request_context_t* self); + + /// + // Tells all renderer processes associated with this context to throw away + // their plugin list cache. If |reload_pages| is true (1) they will also + // reload all pages with plugins. + // cef_request_tContextHandler::OnBeforePluginLoad may be called to rebuild + // the plugin list cache. + /// + void (CEF_CALLBACK *purge_plugin_list_cache)( + struct _cef_request_context_t* self, int reload_pages); } cef_request_context_t; diff --git a/include/capi/cef_request_context_handler_capi.h b/include/capi/cef_request_context_handler_capi.h index 1856ca5e1..c1d91f6ec 100644 --- a/include/capi/cef_request_context_handler_capi.h +++ b/include/capi/cef_request_context_handler_capi.h @@ -67,15 +67,21 @@ typedef struct _cef_request_context_handler_t { struct _cef_request_context_handler_t* self); /// - // Called on the browser process IO thread before a plugin instance is loaded. - // |mime_type| is the mime type of the plugin that will be loaded. + // Called on multiple browser process threads before a plugin instance is + // loaded. |mime_type| is the mime type of the plugin that will be loaded. // |plugin_url| is the content URL that the plugin will load and may be NULL. // |top_origin_url| is the URL for the top-level frame that contains the - // plugin. |plugin_info| includes additional information about the plugin that - // will be loaded. |plugin_policy| is the recommended policy. Modify - // |plugin_policy| and return true (1) to change the policy. Return false (0) - // to use the recommended policy. The default plugin policy can be set at - // runtime using the `--plugin-policy=[allow|detect|block]` command-line flag. + // plugin when loading a specific plugin instance or NULL when building the + // initial list of enabled plugins for 'navigator.plugins' JavaScript state. + // |plugin_info| includes additional information about the plugin that will be + // loaded. |plugin_policy| is the recommended policy. Modify |plugin_policy| + // and return true (1) to change the policy. Return false (0) to use the + // recommended policy. The default plugin policy can be set at runtime using + // the `--plugin-policy=[allow|detect|block]` command-line flag. Decisions to + // mark a plugin as disabled by setting |plugin_policy| to + // PLUGIN_POLICY_DISABLED may be cached when |top_origin_url| is NULL. To + // purge the plugin list cache and potentially trigger new calls to this + // function call cef_request_tContext::PurgePluginListCache. /// int (CEF_CALLBACK *on_before_plugin_load)( struct _cef_request_context_handler_t* self, diff --git a/include/cef_request_context.h b/include/cef_request_context.h index d047c99e1..845fbe822 100644 --- a/include/cef_request_context.h +++ b/include/cef_request_context.h @@ -157,6 +157,15 @@ class CefRequestContext : public virtual CefBase { /// /*--cef()--*/ virtual bool ClearSchemeHandlerFactories() =0; + + /// + // Tells all renderer processes associated with this context to throw away + // their plugin list cache. If |reload_pages| is true they will also reload + // all pages with plugins. CefRequestContextHandler::OnBeforePluginLoad may + // be called to rebuild the plugin list cache. + /// + /*--cef()--*/ + virtual void PurgePluginListCache(bool reload_pages) =0; }; #endif // CEF_INCLUDE_CEF_REQUEST_CONTEXT_H_ diff --git a/include/cef_request_context_handler.h b/include/cef_request_context_handler.h index efe55fa41..c2d3c7af9 100644 --- a/include/cef_request_context_handler.h +++ b/include/cef_request_context_handler.h @@ -61,17 +61,23 @@ class CefRequestContextHandler : public virtual CefBase { virtual CefRefPtr GetCookieManager() { return NULL; } /// - // Called on the browser process IO thread before a plugin instance is loaded. - // |mime_type| is the mime type of the plugin that will be loaded. + // Called on multiple browser process threads before a plugin instance is + // loaded. |mime_type| is the mime type of the plugin that will be loaded. // |plugin_url| is the content URL that the plugin will load and may be empty. // |top_origin_url| is the URL for the top-level frame that contains the - // plugin. |plugin_info| includes additional information about the plugin that - // will be loaded. |plugin_policy| is the recommended policy. Modify - // |plugin_policy| and return true to change the policy. Return false to use - // the recommended policy. The default plugin policy can be set at runtime - // using the `--plugin-policy=[allow|detect|block]` command-line flag. + // plugin when loading a specific plugin instance or empty when building the + // initial list of enabled plugins for 'navigator.plugins' JavaScript state. + // |plugin_info| includes additional information about the plugin that will be + // loaded. |plugin_policy| is the recommended policy. Modify |plugin_policy| + // and return true to change the policy. Return false to use the recommended + // policy. The default plugin policy can be set at runtime using the + // `--plugin-policy=[allow|detect|block]` command-line flag. Decisions to mark + // a plugin as disabled by setting |plugin_policy| to PLUGIN_POLICY_DISABLED + // may be cached when |top_origin_url| is empty. To purge the plugin list + // cache and potentially trigger new calls to this method call + // CefRequestContext::PurgePluginListCache. /// - /*--cef(optional_param=plugin_url)--*/ + /*--cef(optional_param=plugin_url,optional_param=top_origin_url)--*/ virtual bool OnBeforePluginLoad(const CefString& mime_type, const CefString& plugin_url, const CefString& top_origin_url, diff --git a/libcef/browser/browser_context.cc b/libcef/browser/browser_context.cc index 7ceff98b0..a3d0eab23 100644 --- a/libcef/browser/browser_context.cc +++ b/libcef/browser/browser_context.cc @@ -52,7 +52,8 @@ void CefBrowserContext::Initialize() { resource_context_.reset(new CefResourceContext( IsOffTheRecord(), - extensions_enabled ? extension_system_->info_map() : NULL)); + extensions_enabled ? extension_system_->info_map() : NULL, + GetHandler())); BrowserContextDependencyManager::GetInstance()->CreateBrowserContextServices( this); diff --git a/libcef/browser/content_browser_client.cc b/libcef/browser/content_browser_client.cc index edc9eae06..dc8522b5d 100644 --- a/libcef/browser/content_browser_client.cc +++ b/libcef/browser/content_browser_client.cc @@ -19,12 +19,12 @@ #include "libcef/browser/media_capture_devices_dispatcher.h" #include "libcef/browser/pepper/browser_pepper_host_factory.h" #include "libcef/browser/plugins/plugin_info_message_filter.h" +#include "libcef/browser/plugins/plugin_service_filter.h" #include "libcef/browser/printing/printing_message_filter.h" #include "libcef/browser/resource_dispatcher_host_delegate.h" #include "libcef/browser/speech_recognition_manager_delegate.h" #include "libcef/browser/ssl_info_impl.h" #include "libcef/browser/thread_util.h" -#include "libcef/browser/web_plugin_impl.h" #include "libcef/common/cef_messages.h" #include "libcef/common/cef_switches.h" #include "libcef/common/command_line_impl.h" @@ -38,6 +38,7 @@ #include "base/path_service.h" #include "chrome/browser/spellchecker/spellcheck_message_filter.h" #include "chrome/common/chrome_switches.h" +#include "content/browser/plugin_service_impl.h" #include "content/public/browser/access_token_store.h" #include "content/public/browser/browser_ppapi_host.h" #include "content/public/browser/browser_url_handler.h" @@ -358,6 +359,10 @@ int GetCrashSignalFD(const base::CommandLine& command_line) { CefContentBrowserClient::CefContentBrowserClient() : browser_main_parts_(NULL), next_browser_id_(0) { + plugin_service_filter_.reset(new CefPluginServiceFilter); + content::PluginServiceImpl::GetInstance()->SetFilter( + plugin_service_filter_.get()); + last_create_window_params_.opener_process_id = MSG_ROUTING_NONE; } diff --git a/libcef/browser/content_browser_client.h b/libcef/browser/content_browser_client.h index 84d2a68f2..dd2f2b4d4 100644 --- a/libcef/browser/content_browser_client.h +++ b/libcef/browser/content_browser_client.h @@ -32,6 +32,7 @@ class CefResourceDispatcherHostDelegate; class PrefService; namespace content { +class PluginServiceFilter; class SiteInstance; } @@ -172,6 +173,7 @@ class CefContentBrowserClient : public content::ContentBrowserClient { CefBrowserMainParts* browser_main_parts_; + scoped_ptr plugin_service_filter_; scoped_ptr resource_dispatcher_host_delegate_; diff --git a/libcef/browser/plugins/plugin_info_message_filter.cc b/libcef/browser/plugins/plugin_info_message_filter.cc index 3c6370bb8..16ab9badf 100644 --- a/libcef/browser/plugins/plugin_info_message_filter.cc +++ b/libcef/browser/plugins/plugin_info_message_filter.cc @@ -6,9 +6,9 @@ #include "libcef/browser/plugins/plugin_info_message_filter.h" #include "libcef/browser/browser_context.h" +#include "libcef/browser/plugins/plugin_service_filter.h" #include "libcef/browser/web_plugin_impl.h" #include "libcef/common/cef_messages.h" -#include "libcef/common/content_client.h" #include "base/bind.h" #include "base/memory/scoped_ptr.h" @@ -243,83 +243,15 @@ void CefPluginInfoMessageFilter::PluginsLoaded( IPC::Message* reply_msg, const std::vector& plugins) { CefViewHostMsg_GetPluginInfo_Output output; + CefRefPtr handler = + browser_context_->GetHandler(); + // This also fills in |actual_mime_type|. scoped_ptr plugin_metadata; - if (context_.FindEnabledPlugin(params.render_frame_id, params.url, - params.top_origin_url, params.mime_type, - &output.status, &output.plugin, - &output.actual_mime_type, - &plugin_metadata)) { - context_.DecidePluginStatus(params, output.plugin, plugin_metadata.get(), - &output.status); - - const bool is_found = - output.status != CefViewHostMsg_GetPluginInfo_Status::kNotFound && - output.status != CefViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported; - - const bool is_internal_pdf_plugin = - is_found && - (output.plugin.path == CefString(CefContentClient::kPDFPluginPath)); - if (is_internal_pdf_plugin && - output.status != CefViewHostMsg_GetPluginInfo_Status::kAllowed) { - // Always allow the internal PDF plugin to load if it exists. - output.status = CefViewHostMsg_GetPluginInfo_Status::kAllowed; - } - - // Give the handler an opportunity to modify the policy. - CefRefPtr handler = - browser_context_->GetHandler(); - if (handler.get() && - is_found && - !is_internal_pdf_plugin) { - cef_plugin_policy_t plugin_policy = PLUGIN_POLICY_DISABLE; - switch (output.status) { - case CefViewHostMsg_GetPluginInfo_Status::kAllowed: - plugin_policy = PLUGIN_POLICY_ALLOW; - break; - case CefViewHostMsg_GetPluginInfo_Status::kBlocked: - case CefViewHostMsg_GetPluginInfo_Status::kBlockedByPolicy: - case CefViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked: - case CefViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed: - case CefViewHostMsg_GetPluginInfo_Status::kUnauthorized: - plugin_policy = PLUGIN_POLICY_BLOCK; - break; - case CefViewHostMsg_GetPluginInfo_Status::kDisabled: - plugin_policy = PLUGIN_POLICY_DISABLE; - break; - case CefViewHostMsg_GetPluginInfo_Status::kPlayImportantContent: - plugin_policy = PLUGIN_POLICY_DETECT_IMPORTANT; - break; - default: - NOTREACHED(); - break; - } - - CefRefPtr pluginInfo( - new CefWebPluginInfoImpl(output.plugin)); - if (handler->OnBeforePluginLoad(output.actual_mime_type, - params.url.spec(), - params.top_origin_url.spec(), - pluginInfo.get(), - &plugin_policy)) { - switch (plugin_policy) { - case PLUGIN_POLICY_ALLOW: - output.status = CefViewHostMsg_GetPluginInfo_Status::kAllowed; - break; - case PLUGIN_POLICY_DETECT_IMPORTANT: - output.status = - CefViewHostMsg_GetPluginInfo_Status::kPlayImportantContent; - break; - case PLUGIN_POLICY_BLOCK: - output.status = CefViewHostMsg_GetPluginInfo_Status::kBlocked; - break; - case PLUGIN_POLICY_DISABLE: - output.status = CefViewHostMsg_GetPluginInfo_Status::kDisabled; - break; - } - } - } - } + context_.FindEnabledPlugin(params, handler.get(), + &output.status, &output.plugin, + &output.actual_mime_type, + &plugin_metadata); if (plugin_metadata) { output.group_identifier = plugin_metadata->identifier(); @@ -488,10 +420,8 @@ void CefPluginInfoMessageFilter::Context::DecidePluginStatus( } bool CefPluginInfoMessageFilter::Context::FindEnabledPlugin( - int render_frame_id, - const GURL& url, - const GURL& top_origin_url, - const std::string& mime_type, + const GetPluginInfo_Params& params, + CefRequestContextHandler* handler, CefViewHostMsg_GetPluginInfo_Status* status, WebPluginInfo* plugin, std::string* actual_mime_type, @@ -502,7 +432,8 @@ bool CefPluginInfoMessageFilter::Context::FindEnabledPlugin( std::vector matching_plugins; std::vector mime_types; PluginService::GetInstance()->GetPluginInfoArray( - url, mime_type, allow_wildcard, &matching_plugins, &mime_types); + params.url, params.mime_type, allow_wildcard, &matching_plugins, + &mime_types); if (matching_plugins.empty()) { *status = CefViewHostMsg_GetPluginInfo_Status::kNotFound; #if defined(OS_WIN) || defined(OS_MACOSX) @@ -511,39 +442,48 @@ bool CefPluginInfoMessageFilter::Context::FindEnabledPlugin( // could be a not-yet-installed Pepper plugin. To avoid notifying on // these types, bail early based on a blacklist of pepper mime types. for (auto pepper_mime_type : kPepperPluginMimeTypes) - if (pepper_mime_type == mime_type) + if (pepper_mime_type == params.mime_type) return false; } #endif return false; } - content::PluginServiceFilter* filter = - PluginService::GetInstance()->GetFilter(); + CefPluginServiceFilter* filter = static_cast( + PluginService::GetInstance()->GetFilter()); + DCHECK(filter); + + CefViewHostMsg_GetPluginInfo_Status first_status = *status; + size_t i = 0; for (; i < matching_plugins.size(); ++i) { - if (!filter || filter->IsPluginAvailable(render_process_id_, - render_frame_id, - resource_context_, - url, - top_origin_url, - &matching_plugins[i])) { + *plugin = matching_plugins[i]; + *actual_mime_type = mime_types[i]; + *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin); + + DecidePluginStatus(params, *plugin, (*plugin_metadata).get(), status); + if (filter->IsPluginAvailable(handler, + params.url, + params.top_origin_url, + plugin, + status)) { break; } + + if (i == 0) + first_status = *status; } // If we broke out of the loop, we have found an enabled plugin. bool enabled = i < matching_plugins.size(); if (!enabled) { - // Otherwise, we only found disabled plugins, so we take the first one. - i = 0; - *status = CefViewHostMsg_GetPluginInfo_Status::kDisabled; - } - - *plugin = matching_plugins[i]; - *actual_mime_type = mime_types[i]; - if (plugin_metadata) + // Otherwise, we only found disabled/not-found plugins, so we take the first + // one. + *plugin = matching_plugins[0]; + *actual_mime_type = mime_types[0]; *plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin); + *status = first_status; + } return enabled; } diff --git a/libcef/browser/plugins/plugin_info_message_filter.h b/libcef/browser/plugins/plugin_info_message_filter.h index 433dc6123..e90f092ac 100644 --- a/libcef/browser/plugins/plugin_info_message_filter.h +++ b/libcef/browser/plugins/plugin_info_message_filter.h @@ -20,6 +20,7 @@ #include "content/public/browser/browser_message_filter.h" class CefBrowserContext; +class CefRequestContextHandler; struct CefViewHostMsg_GetPluginInfo_Output; enum class CefViewHostMsg_GetPluginInfo_Status; class GURL; @@ -50,10 +51,8 @@ class CefPluginInfoMessageFilter : public content::BrowserMessageFilter { const content::WebPluginInfo& plugin, const PluginMetadata* plugin_metadata, CefViewHostMsg_GetPluginInfo_Status* status) const; - bool FindEnabledPlugin(int render_frame_id, - const GURL& url, - const GURL& top_origin_url, - const std::string& mime_type, + bool FindEnabledPlugin(const GetPluginInfo_Params& params, + CefRequestContextHandler* handler, CefViewHostMsg_GetPluginInfo_Status* status, content::WebPluginInfo* plugin, std::string* actual_mime_type, diff --git a/libcef/browser/plugins/plugin_service_filter.cc b/libcef/browser/plugins/plugin_service_filter.cc new file mode 100644 index 000000000..ea3212c93 --- /dev/null +++ b/libcef/browser/plugins/plugin_service_filter.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2015 The Chromium Embedded Framework 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 "libcef/browser/plugins/plugin_service_filter.h" + +#include "include/cef_request_context_handler.h" +#include "libcef/browser/browser_host_impl.h" +#include "libcef/browser/resource_context.h" +#include "libcef/browser/thread_util.h" +#include "libcef/browser/web_plugin_impl.h" +#include "libcef/common/cef_messages.h" +#include "libcef/common/content_client.h" + +CefPluginServiceFilter::CefPluginServiceFilter() { +} + +bool CefPluginServiceFilter::IsPluginAvailable( + int render_process_id, + int render_frame_id, + const void* context, + const GURL& url, + const GURL& policy_url, + content::WebPluginInfo* plugin) { + CefRefPtr handler = + reinterpret_cast(context)->GetHandler(); + + CefViewHostMsg_GetPluginInfo_Status status = + CefViewHostMsg_GetPluginInfo_Status::kAllowed; + return IsPluginAvailable(handler.get(), url, policy_url, plugin, &status); +} + +bool CefPluginServiceFilter::CanLoadPlugin(int render_process_id, + const base::FilePath& path) { + return true; +} + +bool CefPluginServiceFilter::IsPluginAvailable( + CefRequestContextHandler* handler, + const GURL& url, + const GURL& policy_url, + content::WebPluginInfo* plugin, + CefViewHostMsg_GetPluginInfo_Status* status) { + if (*status == CefViewHostMsg_GetPluginInfo_Status::kNotFound || + *status == CefViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported) { + // The plugin does not exist so no need to query the handler. + return false; + } + + if (plugin->path == CefString(CefContentClient::kPDFPluginPath)) { + // Always allow the internal PDF plugin to load. + *status = CefViewHostMsg_GetPluginInfo_Status::kAllowed; + return true; + } + + if (handler) { + CefRefPtr pluginInfo( + new CefWebPluginInfoImpl(*plugin)); + + cef_plugin_policy_t plugin_policy = PLUGIN_POLICY_ALLOW; + switch (*status) { + case CefViewHostMsg_GetPluginInfo_Status::kAllowed: + plugin_policy = PLUGIN_POLICY_ALLOW; + break; + case CefViewHostMsg_GetPluginInfo_Status::kBlocked: + case CefViewHostMsg_GetPluginInfo_Status::kBlockedByPolicy: + case CefViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked: + case CefViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed: + case CefViewHostMsg_GetPluginInfo_Status::kUnauthorized: + plugin_policy = PLUGIN_POLICY_BLOCK; + break; + case CefViewHostMsg_GetPluginInfo_Status::kDisabled: + plugin_policy = PLUGIN_POLICY_DISABLE; + break; + case CefViewHostMsg_GetPluginInfo_Status::kPlayImportantContent: + plugin_policy = PLUGIN_POLICY_DETECT_IMPORTANT; + break; + default: + NOTREACHED(); + break; + } + + if (handler->OnBeforePluginLoad(plugin->mime_types[0].mime_type, + url.possibly_invalid_spec(), + policy_url.possibly_invalid_spec(), + pluginInfo.get(), + &plugin_policy)) { + switch (plugin_policy) { + case PLUGIN_POLICY_ALLOW: + *status = CefViewHostMsg_GetPluginInfo_Status::kAllowed; + break; + case PLUGIN_POLICY_DETECT_IMPORTANT: + *status = CefViewHostMsg_GetPluginInfo_Status::kPlayImportantContent; + break; + case PLUGIN_POLICY_BLOCK: + *status = CefViewHostMsg_GetPluginInfo_Status::kBlocked; + break; + case PLUGIN_POLICY_DISABLE: + *status = CefViewHostMsg_GetPluginInfo_Status::kDisabled; + break; + } + } + } + + return (*status != CefViewHostMsg_GetPluginInfo_Status::kDisabled); +} diff --git a/libcef/browser/plugins/plugin_service_filter.h b/libcef/browser/plugins/plugin_service_filter.h new file mode 100644 index 000000000..1de4c17a3 --- /dev/null +++ b/libcef/browser/plugins/plugin_service_filter.h @@ -0,0 +1,46 @@ +// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that can +// be found in the LICENSE file. + +#ifndef CEF_LIBCEF_BROWSER_PLUGINS_PLUGIN_SERVICE_FILTER_H_ +#define CEF_LIBCEF_BROWSER_PLUGINS_PLUGIN_SERVICE_FILTER_H_ + +#include "content/public/browser/plugin_service_filter.h" + +#include "include/internal/cef_types.h" + +#include "base/macros.h" + +class CefRequestContextHandler; +enum class CefViewHostMsg_GetPluginInfo_Status; + +class CefPluginServiceFilter : public content::PluginServiceFilter { + public: + CefPluginServiceFilter(); + + // Called whenever the plugin list is queried. For example, when choosing the + // plugin to handle a mime type or when determining the plugins that will be + // exposed to JavaScript via 'navigator.plugins'. + bool IsPluginAvailable(int render_process_id, + int render_frame_id, + const void* context, + const GURL& url, + const GURL& policy_url, + content::WebPluginInfo* plugin) override; + + bool CanLoadPlugin(int render_process_id, + const base::FilePath& path) override; + + // Returns false if the plugin is not found or disabled. May call + // CefRequestContextHandler::OnBeforePluginLoad if possible/necessary. + bool IsPluginAvailable(CefRequestContextHandler* handler, + const GURL& url, + const GURL& policy_url, + content::WebPluginInfo* plugin, + CefViewHostMsg_GetPluginInfo_Status* status); + + private: + DISALLOW_COPY_AND_ASSIGN(CefPluginServiceFilter); +}; + +#endif // CEF_LIBCEF_BROWSER_PLUGINS_PLUGIN_SERVICE_FILTER_H_ diff --git a/libcef/browser/request_context_impl.cc b/libcef/browser/request_context_impl.cc index a00422228..faac5c860 100644 --- a/libcef/browser/request_context_impl.cc +++ b/libcef/browser/request_context_impl.cc @@ -9,8 +9,10 @@ #include "libcef/browser/context.h" #include "libcef/browser/cookie_manager_impl.h" #include "libcef/browser/thread_util.h" + #include "base/atomic_sequence_num.h" #include "base/logging.h" +#include "content/public/browser/plugin_service.h" using content::BrowserThread; @@ -283,6 +285,13 @@ bool CefRequestContextImpl::ClearSchemeHandlerFactories() { return true; } +void CefRequestContextImpl::PurgePluginListCache(bool reload_pages) { + GetBrowserContext( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), + base::Bind(&CefRequestContextImpl::PurgePluginListCacheInternal, + this, reload_pages)); +} + CefRequestContextImpl::CefRequestContextImpl( scoped_refptr browser_context) : browser_context_(browser_context), @@ -375,3 +384,11 @@ void CefRequestContextImpl::ClearSchemeHandlerFactoriesInternal( CEF_REQUIRE_IOT(); request_context->request_manager()->ClearFactories(); } + +void CefRequestContextImpl::PurgePluginListCacheInternal( + bool reload_pages, + scoped_refptr browser_context) { + CEF_REQUIRE_UIT(); + content::PluginService::GetInstance()->PurgePluginListCache( + browser_context.get(), false); +} diff --git a/libcef/browser/request_context_impl.h b/libcef/browser/request_context_impl.h index b3a5513ae..9f28ba51b 100644 --- a/libcef/browser/request_context_impl.h +++ b/libcef/browser/request_context_impl.h @@ -60,6 +60,7 @@ class CefRequestContextImpl : public CefRequestContext { const CefString& domain_name, CefRefPtr factory) override; bool ClearSchemeHandlerFactories() override; + void PurgePluginListCache(bool reload_pages) override; const CefRequestContextSettings& settings() const { return settings_; } @@ -85,9 +86,12 @@ class CefRequestContextImpl : public CefRequestContext { const CefString& scheme_name, const CefString& domain_name, CefRefPtr factory, - scoped_refptr request_context); + scoped_refptr request_context); void ClearSchemeHandlerFactoriesInternal( - scoped_refptr request_context); + scoped_refptr request_context); + void PurgePluginListCacheInternal( + bool reload_pages, + scoped_refptr browser_context); scoped_refptr browser_context_; CefRequestContextSettings settings_; diff --git a/libcef/browser/resource_context.cc b/libcef/browser/resource_context.cc index d3c10abf9..9a253a903 100644 --- a/libcef/browser/resource_context.cc +++ b/libcef/browser/resource_context.cc @@ -22,9 +22,11 @@ CefResourceContext::CefResourceContext( bool is_off_the_record, - extensions::InfoMap* extension_info_map) + extensions::InfoMap* extension_info_map, + CefRefPtr handler) : is_off_the_record_(is_off_the_record), - extension_info_map_(extension_info_map) { + extension_info_map_(extension_info_map), + handler_(handler) { } CefResourceContext::~CefResourceContext() { diff --git a/libcef/browser/resource_context.h b/libcef/browser/resource_context.h index 63b6d06f9..3bd21ccf9 100644 --- a/libcef/browser/resource_context.h +++ b/libcef/browser/resource_context.h @@ -6,6 +6,8 @@ #define CEF_LIBCEF_BROWSER_RESOURCE_CONTEXT_H_ #pragma once +#include "include/cef_request_context_handler.h" + #include "content/public/browser/resource_context.h" #include "extensions/browser/info_map.h" @@ -22,7 +24,8 @@ class CefURLRequestContextGetter; class CefResourceContext : public content::ResourceContext { public: CefResourceContext(bool is_off_the_record, - extensions::InfoMap* extension_info_map); + extensions::InfoMap* extension_info_map, + CefRefPtr handler); ~CefResourceContext() override; // ResourceContext implementation. @@ -38,12 +41,14 @@ class CefResourceContext : public content::ResourceContext { const extensions::InfoMap* GetExtensionInfoMap() const { return extension_info_map_.get(); } + CefRefPtr GetHandler() const { return handler_; } private: scoped_refptr getter_; bool is_off_the_record_; scoped_refptr extension_info_map_; + CefRefPtr handler_; DISALLOW_COPY_AND_ASSIGN(CefResourceContext); }; diff --git a/libcef_dll/cpptoc/request_context_cpptoc.cc b/libcef_dll/cpptoc/request_context_cpptoc.cc index 22c17b4ff..f113cfda0 100644 --- a/libcef_dll/cpptoc/request_context_cpptoc.cc +++ b/libcef_dll/cpptoc/request_context_cpptoc.cc @@ -226,6 +226,19 @@ int CEF_CALLBACK request_context_clear_scheme_handler_factories( return _retval; } +void CEF_CALLBACK request_context_purge_plugin_list_cache( + struct _cef_request_context_t* self, int reload_pages) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefRequestContextCppToC::Get(self)->PurgePluginListCache( + reload_pages?true:false); +} + } // namespace @@ -243,6 +256,8 @@ CefRequestContextCppToC::CefRequestContextCppToC() { request_context_register_scheme_handler_factory; GetStruct()->clear_scheme_handler_factories = request_context_clear_scheme_handler_factories; + GetStruct()->purge_plugin_list_cache = + request_context_purge_plugin_list_cache; } template<> CefRefPtr CefCppToCOnBeforePluginLoad( diff --git a/libcef_dll/ctocpp/request_context_ctocpp.cc b/libcef_dll/ctocpp/request_context_ctocpp.cc index e981b3ca2..d56bf4bd3 100644 --- a/libcef_dll/ctocpp/request_context_ctocpp.cc +++ b/libcef_dll/ctocpp/request_context_ctocpp.cc @@ -210,6 +210,18 @@ bool CefRequestContextCToCpp::ClearSchemeHandlerFactories() { return _retval?true:false; } +void CefRequestContextCToCpp::PurgePluginListCache(bool reload_pages) { + cef_request_context_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, purge_plugin_list_cache)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->purge_plugin_list_cache(_struct, + reload_pages); +} + // 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 6a1cfe722..8dedb6aa5 100644 --- a/libcef_dll/ctocpp/request_context_ctocpp.h +++ b/libcef_dll/ctocpp/request_context_ctocpp.h @@ -44,6 +44,7 @@ class CefRequestContextCToCpp const CefString& domain_name, CefRefPtr factory) OVERRIDE; bool ClearSchemeHandlerFactories() OVERRIDE; + void PurgePluginListCache(bool reload_pages) OVERRIDE; }; #endif // USING_CEF_SHARED diff --git a/libcef_dll/ctocpp/request_context_handler_ctocpp.cc b/libcef_dll/ctocpp/request_context_handler_ctocpp.cc index c6f884665..31727ea1e 100644 --- a/libcef_dll/ctocpp/request_context_handler_ctocpp.cc +++ b/libcef_dll/ctocpp/request_context_handler_ctocpp.cc @@ -45,10 +45,6 @@ bool CefRequestContextHandlerCToCpp::OnBeforePluginLoad( DCHECK(!mime_type.empty()); if (mime_type.empty()) return false; - // Verify param: top_origin_url; type: string_byref_const - DCHECK(!top_origin_url.empty()); - if (top_origin_url.empty()) - return false; // Verify param: plugin_info; type: refptr_diff DCHECK(plugin_info.get()); if (!plugin_info.get()) @@ -57,7 +53,7 @@ bool CefRequestContextHandlerCToCpp::OnBeforePluginLoad( DCHECK(plugin_policy); if (!plugin_policy) return false; - // Unverified params: plugin_url + // Unverified params: plugin_url, top_origin_url // Execute int _retval = _struct->on_before_plugin_load(_struct, diff --git a/tests/cefclient/browser/root_window_manager.cc b/tests/cefclient/browser/root_window_manager.cc index b4745c6cc..af89ccda1 100644 --- a/tests/cefclient/browser/root_window_manager.cc +++ b/tests/cefclient/browser/root_window_manager.cc @@ -32,7 +32,9 @@ class ClientRequestContextHandler : public CefRequestContextHandler { *plugin_policy = PLUGIN_POLICY_ALLOW; return true; } - return false; + + *plugin_policy = PLUGIN_POLICY_DISABLE; + return true; } private: diff --git a/tests/unittests/plugin_unittest.cc b/tests/unittests/plugin_unittest.cc index 1578563d3..fb509580f 100644 --- a/tests/unittests/plugin_unittest.cc +++ b/tests/unittests/plugin_unittest.cc @@ -83,6 +83,10 @@ class PluginTestHandler : public RoutingTestHandler, // plugin via the context menu. GLOBAL_DISABLE_HIDE, + // Global context with handler that removes the plugin from the + // `navigator.plugins` list and consequently disables plugin load. + GLOBAL_NO_LIST, + // Custom context with no handler. CUSTOM_NO_HANDLER, @@ -100,6 +104,10 @@ class PluginTestHandler : public RoutingTestHandler, // Custom context with handler that disables plugin load. Then, hide the // plugin via the context menu. CUSTOM_DISABLE_HIDE, + + // Custom context with handler that removes the plugin from the + // `navigator.plugins` list and consequently disables plugin load. + CUSTOM_NO_LIST, }; class RequestContextHandler : public CefRequestContextHandler { @@ -113,6 +121,20 @@ class PluginTestHandler : public RoutingTestHandler, CefRefPtr plugin_info, PluginPolicy* plugin_policy) override { const std::string& mime_type_str = mime_type; + + if (top_origin_url.empty()) { + handler_->got_on_before_plugin_empty_origin_.yes(); + + if (mime_type_str == "application/pdf" && handler_->HasNoList()) { + // Remove the PDF plugin from the `navigator.plugins` list. + *plugin_policy = PLUGIN_POLICY_DISABLE; + return true; + } else { + // Ignore requests for building the plugin list. + return false; + } + } + if (mime_type_str == "application/pdf") { if (!handler_->got_on_before_plugin_load_pdf1_) handler_->got_on_before_plugin_load_pdf1_.yes(); @@ -167,7 +189,7 @@ class PluginTestHandler : public RoutingTestHandler, // Using the global request context, either directly or with a custom handler. bool HasGlobalRequestContext() const { - return mode_ >= GLOBAL_DEFAULT && mode_ <= GLOBAL_DISABLE_HIDE; + return mode_ >= GLOBAL_DEFAULT && mode_ <= GLOBAL_NO_LIST; } // Should allow the plugin load via the custom handler. @@ -185,7 +207,14 @@ class PluginTestHandler : public RoutingTestHandler, // Should disable the plugin load via the custom handler. bool HasDisable() const { - return mode_ == GLOBAL_DISABLE_HIDE || mode_ == CUSTOM_DISABLE_HIDE; + return mode_ == GLOBAL_DISABLE_HIDE || + mode_ == CUSTOM_DISABLE_HIDE; + } + + // Should exclude the plugin from the `navigator.plugins` list. + bool HasNoList() const { + return mode_ == GLOBAL_NO_LIST || + mode_ == CUSTOM_NO_LIST; } // Should load the plugin via the context menu. @@ -205,6 +234,18 @@ class PluginTestHandler : public RoutingTestHandler, return "document.getElementsByTagName('embed')[0]"; } + void WaitForNavigatorPlugins(CefRefPtr frame) const { + // Test if the `navigator.plugins` list includes the PDF extension. + const std::string& code = + " if (navigator.plugins['Chromium PDF Viewer'].filename == " + " 'mhjfbmdgcfjbbpaeojofohoefgiehjai') {" + " window.testQuery({request:'pdf_plugin_found'});" + "} else {" + " window.testQuery({request:'pdf_plugin_missing'});" + "}"; + frame->ExecuteJavaScript(code, frame->GetURL(), 0); + } + void WaitForPlaceholderLoad(CefRefPtr frame) const { // Waits for the placeholder to load. // See LoadablePluginPlaceholder::DidFinishLoadingCallback and @@ -280,6 +321,10 @@ class PluginTestHandler : public RoutingTestHandler, frame->ExecuteJavaScript(code, frame->GetURL(), 0); } + void EndTest() { + CefPostTask(TID_UI, base::Bind(&PluginTestHandler::DestroyTest, this)); + } + CefRefPtr GetContextMenuHandler() override { return this; } @@ -352,15 +397,13 @@ class PluginTestHandler : public RoutingTestHandler, NOTREACHED(); } - if (HasBlock() || HasDisable()) { - if (is_pdf1) { - // Wait for the plugin placeholder for the first PDF file to load. The - // test will continue from OnQuery. - WaitForPlaceholderLoad(frame); - } + if (HasNoList()) { + // If the plugin is not listed then the PDF documents will never load. + EXPECT_STREQ(kPdfHtmlUrl, url.c_str()); + WaitForNavigatorPlugins(frame); } else if (is_pdf1) { - // Wait for the first PDF file to load. - WaitForPluginLoad(frame); + // The first PDF document has loaded. + WaitForNavigatorPlugins(frame); } } @@ -370,7 +413,24 @@ class PluginTestHandler : public RoutingTestHandler, const CefString& request, bool persistent, CefRefPtr callback) override { - if (request == "placeholder_ready") { + if (request == "pdf_plugin_found" || request == "pdf_plugin_missing") { + if (request == "pdf_plugin_found") + got_pdf_plugin_found_.yes(); + else + got_pdf_plugin_missing_.yes(); + + if (HasNoList()) { + // The plugin will not load. End the test. + EndTest(); + } else if (HasBlock() || HasDisable()) { + // Wait for the plugin placeholder for the first PDF file to load. The + // test will continue from OnQuery. + WaitForPlaceholderLoad(frame); + } else { + // Wait for the first PDF file to load. + WaitForPluginLoad(frame); + } + } else if (request == "placeholder_ready") { EXPECT_FALSE(got_placeholder_ready_); got_placeholder_ready_.yes(); @@ -382,18 +442,16 @@ class PluginTestHandler : public RoutingTestHandler, EXPECT_FALSE(got_placeholder_hidden_); got_placeholder_hidden_.yes(); - // The plugin placeholder has been hidden. - CefPostTask(TID_UI, - base::Bind(&PluginTestHandler::DestroyTest, this)); + // The plugin placeholder has been hidden. End the test. + EndTest(); } else if (request == "plugin_ready") { EXPECT_FALSE(got_plugin_ready_); got_plugin_ready_.yes(); // The plugin has loaded the PDF file. if (got_context_menu_dismissed_) { - // After context menu display. Destroy the test. - CefPostTask(TID_UI, - base::Bind(&PluginTestHandler::DestroyTest, this)); + // After context menu display. End the test. + EndTest(); } else { // Trigger the context menu. CefPostTask(TID_UI, @@ -469,7 +527,7 @@ class PluginTestHandler : public RoutingTestHandler, return; } - CefPostTask(TID_UI, base::Bind(&PluginTestHandler::DestroyTest, this)); + EndTest(); } void DestroyTest() override { @@ -495,18 +553,33 @@ class PluginTestHandler : public RoutingTestHandler, EXPECT_FALSE(got_plugin_ready_); } - EXPECT_TRUE(got_run_context_menu_); - EXPECT_TRUE(got_context_menu_dismissed_); + if (HasRequestContextHandler()) + EXPECT_TRUE(got_on_before_plugin_empty_origin_); + + if (HasNoList()) { + EXPECT_FALSE(got_pdf_plugin_found_); + EXPECT_TRUE(got_pdf_plugin_missing_); + EXPECT_FALSE(got_run_context_menu_); + EXPECT_FALSE(got_context_menu_dismissed_); + } else { + EXPECT_TRUE(got_pdf_plugin_found_); + EXPECT_FALSE(got_pdf_plugin_missing_); + EXPECT_TRUE(got_run_context_menu_); + EXPECT_TRUE(got_context_menu_dismissed_); + } if (url_ == kPdfHtmlUrl) { // The HTML file will load the PDF twice in iframes. EXPECT_TRUE(got_on_load_end_html_); - EXPECT_TRUE(got_on_load_end_pdf1_); - EXPECT_TRUE(got_on_load_end_pdf2_); - if (HasRequestContextHandler()) { - EXPECT_TRUE(got_on_before_plugin_load_pdf1_); - EXPECT_TRUE(got_on_before_plugin_load_pdf2_); + if (!HasNoList()) { + EXPECT_TRUE(got_on_load_end_pdf1_); + EXPECT_TRUE(got_on_load_end_pdf2_); + + if (HasRequestContextHandler()) { + EXPECT_TRUE(got_on_before_plugin_load_pdf1_); + EXPECT_TRUE(got_on_before_plugin_load_pdf2_); + } } } else if (url_ == kPdfDirectUrl) { // Load the PDF file directly. @@ -522,7 +595,7 @@ class PluginTestHandler : public RoutingTestHandler, NOTREACHED(); } - if (!HasRequestContextHandler()) { + if (!HasRequestContextHandler() || HasNoList()) { EXPECT_FALSE(got_on_before_plugin_load_pdf1_); EXPECT_FALSE(got_on_before_plugin_load_pdf2_); } @@ -533,11 +606,14 @@ class PluginTestHandler : public RoutingTestHandler, const Mode mode_; const std::string url_; + TrackCallback got_on_before_plugin_empty_origin_; TrackCallback got_on_before_plugin_load_pdf1_; TrackCallback got_on_before_plugin_load_pdf2_; TrackCallback got_on_load_end_html_; TrackCallback got_on_load_end_pdf1_; TrackCallback got_on_load_end_pdf2_; + TrackCallback got_pdf_plugin_found_; + TrackCallback got_pdf_plugin_missing_; TrackCallback got_placeholder_ready_; TrackCallback got_placeholder_hidden_; TrackCallback got_plugin_ready_; @@ -572,6 +648,7 @@ RUN_TEST(GlobalBlockThenHidePdfDirect, GLOBAL_BLOCK_HIDE, kPdfDirectUrl); RUN_TEST(GlobalBlockThenHidePdfHtml, GLOBAL_BLOCK_HIDE, kPdfHtmlUrl); RUN_TEST(GlobalDisableThenHidePdfDirect, GLOBAL_DISABLE_HIDE, kPdfDirectUrl); RUN_TEST(GlobalDisableThenHidePdfHtml, GLOBAL_DISABLE_HIDE, kPdfHtmlUrl); +RUN_TEST(GlobalNoListHtml, GLOBAL_NO_LIST, kPdfHtmlUrl); RUN_TEST(CustomNoHandlerPdfDirect, CUSTOM_NO_HANDLER, kPdfDirectUrl); RUN_TEST(CustomNoHandlerPdfHtml, CUSTOM_NO_HANDLER, kPdfHtmlUrl); @@ -583,6 +660,7 @@ RUN_TEST(CustomBlockThenHidePdfDirect, CUSTOM_BLOCK_HIDE, kPdfDirectUrl); RUN_TEST(CustomBlockThenHidePdfHtml, CUSTOM_BLOCK_HIDE, kPdfHtmlUrl); RUN_TEST(CustomDisableThenHidePdfDirect, CUSTOM_DISABLE_HIDE, kPdfDirectUrl); RUN_TEST(CustomDisableThenHidePdfHtml, CUSTOM_DISABLE_HIDE, kPdfHtmlUrl); +RUN_TEST(CustomNoListHtml, CUSTOM_NO_LIST, kPdfHtmlUrl); // Entry point for creating plugin browser test objects.