mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
Add plugin placeholder and policy support (issue #1708)
- Default plugin loading policy can be specified using the new `--plugin-policy=[allow|block|detect]` command-line flag. - Move CefRequestHandler::OnBeforePluginLoad to CefRequestContextHandler and add a new policy argument that supports different actions (allow, block, detect, disable) on a per-plugin-instance basis. - Add CefContextMenuHandler::RunContextMenu for providing a custom context menu implementation. - Add CefResourceBundleHandler::GetDataResourceForScale for returning scaled resources (issue #1272). - Add CefResourceBundle for retrieving resources from the resource bundle (*.pak) files loaded by CEF during startup or via the CefResourceBundleHandler. - Linux: Fix Debug build IO access warning with CefGetMimeType. - cef_unittests: Move the refcounting implementation from TestHandler to subclasses in order to support interface inheritance from subclasses.
This commit is contained in:
@@ -59,7 +59,7 @@ void CefBrowserContext::Initialize() {
|
||||
|
||||
// Spell checking support and possibly other subsystems retrieve the
|
||||
// PrefService associated with a BrowserContext via UserPrefs::Get().
|
||||
PrefService* pref_service = CefContentBrowserClient::Get()->pref_service();
|
||||
PrefService* pref_service = GetPrefs();
|
||||
DCHECK(pref_service);
|
||||
user_prefs::UserPrefs::Set(this, pref_service);
|
||||
|
||||
|
@@ -104,6 +104,9 @@
|
||||
// CefURLRequestContextGetter* destruction.
|
||||
*/
|
||||
|
||||
class HostContentSettingsMap;
|
||||
class PrefService;
|
||||
|
||||
namespace extensions {
|
||||
class CefExtensionSystem;
|
||||
}
|
||||
@@ -142,6 +145,12 @@ class CefBrowserContext
|
||||
content::ProtocolHandlerMap* protocol_handlers,
|
||||
content::URLRequestInterceptorScopedVector request_interceptors) = 0;
|
||||
|
||||
// Settings for plugins and extensions.
|
||||
virtual HostContentSettingsMap* GetHostContentSettingsMap() = 0;
|
||||
|
||||
// Preferences.
|
||||
virtual PrefService* GetPrefs() = 0;
|
||||
|
||||
CefResourceContext* resource_context() const {
|
||||
return resource_context_.get();
|
||||
}
|
||||
|
@@ -13,13 +13,17 @@
|
||||
#include "libcef/browser/permission_manager.h"
|
||||
#include "libcef/browser/ssl_host_state_delegate.h"
|
||||
#include "libcef/browser/thread_util.h"
|
||||
#include "libcef/common/cef_switches.h"
|
||||
#include "libcef/common/extensions/extensions_util.h"
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/lazy_instance.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
#include "chrome/browser/net/proxy_service_factory.h"
|
||||
#include "components/content_settings/core/browser/host_content_settings_map.h"
|
||||
#include "components/guest_view/browser/guest_view_manager.h"
|
||||
#include "content/public/browser/download_manager.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
@@ -129,6 +133,9 @@ CefBrowserContextImpl::CefBrowserContextImpl(
|
||||
CefBrowserContextImpl::~CefBrowserContextImpl() {
|
||||
pref_proxy_config_tracker_->DetachFromPrefService();
|
||||
|
||||
if (host_content_settings_map_.get())
|
||||
host_content_settings_map_->ShutdownOnUIThread();
|
||||
|
||||
// Delete the download manager delegate here because otherwise we'll crash
|
||||
// when it's accessed from the content::BrowserContext destructor.
|
||||
if (download_manager_delegate_.get())
|
||||
@@ -164,7 +171,7 @@ void CefBrowserContextImpl::Initialize() {
|
||||
// Initialize proxy configuration tracker.
|
||||
pref_proxy_config_tracker_.reset(
|
||||
ProxyServiceFactory::CreatePrefProxyConfigTrackerOfLocalState(
|
||||
CefContentBrowserClient::Get()->pref_service()));
|
||||
GetPrefs()));
|
||||
|
||||
// Create the CefURLRequestContextGetterImpl via an indirect call to
|
||||
// CreateRequestContext. Triggers a call to CefURLRequestContextGetterImpl::
|
||||
@@ -340,3 +347,37 @@ net::URLRequestContextGetter*
|
||||
content::URLRequestInterceptorScopedVector request_interceptors) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HostContentSettingsMap* CefBrowserContextImpl::GetHostContentSettingsMap() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
if (!host_content_settings_map_.get()) {
|
||||
// The |incognito| argument is intentionally set to false as it otherwise
|
||||
// limits the types of values that can be stored in the settings map (for
|
||||
// example, default values set via DefaultProvider::SetWebsiteSetting).
|
||||
host_content_settings_map_ = new HostContentSettingsMap(GetPrefs(), false);
|
||||
|
||||
// Change the default plugin policy.
|
||||
const base::CommandLine* command_line =
|
||||
base::CommandLine::ForCurrentProcess();
|
||||
const std::string& plugin_policy_str =
|
||||
command_line->GetSwitchValueASCII(switches::kPluginPolicy);
|
||||
if (!plugin_policy_str.empty()) {
|
||||
ContentSetting plugin_policy = CONTENT_SETTING_ALLOW;
|
||||
if (base::LowerCaseEqualsASCII(plugin_policy_str,
|
||||
switches::kPluginPolicy_Detect)) {
|
||||
plugin_policy = CONTENT_SETTING_DETECT_IMPORTANT_CONTENT;
|
||||
} else if (base::LowerCaseEqualsASCII(plugin_policy_str,
|
||||
switches::kPluginPolicy_Block)) {
|
||||
plugin_policy = CONTENT_SETTING_BLOCK;
|
||||
}
|
||||
host_content_settings_map_->SetDefaultContentSetting(
|
||||
CONTENT_SETTINGS_TYPE_PLUGINS, plugin_policy);
|
||||
}
|
||||
}
|
||||
return host_content_settings_map_.get();
|
||||
}
|
||||
|
||||
PrefService* CefBrowserContextImpl::GetPrefs() {
|
||||
// TODO(cef): Perhaps use per-context settings.
|
||||
return CefContentBrowserClient::Get()->pref_service();
|
||||
}
|
||||
|
@@ -87,6 +87,8 @@ class CefBrowserContextImpl : public CefBrowserContext {
|
||||
content::ProtocolHandlerMap* protocol_handlers,
|
||||
content::URLRequestInterceptorScopedVector request_interceptors)
|
||||
override;
|
||||
HostContentSettingsMap* GetHostContentSettingsMap() override;
|
||||
PrefService* GetPrefs() override;
|
||||
|
||||
// Guaranteed to exist once this object has been initialized.
|
||||
scoped_refptr<CefURLRequestContextGetterImpl> request_context() const {
|
||||
@@ -115,6 +117,7 @@ class CefBrowserContextImpl : public CefBrowserContext {
|
||||
scoped_refptr<CefURLRequestContextGetterImpl> url_request_getter_;
|
||||
scoped_ptr<content::PermissionManager> permission_manager_;
|
||||
scoped_ptr<CefSSLHostStateDelegate> ssl_host_state_delegate_;
|
||||
scoped_refptr<HostContentSettingsMap> host_content_settings_map_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CefBrowserContextImpl);
|
||||
};
|
||||
|
@@ -169,3 +169,11 @@ net::URLRequestContextGetter*
|
||||
content::URLRequestInterceptorScopedVector request_interceptors) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HostContentSettingsMap* CefBrowserContextProxy::GetHostContentSettingsMap() {
|
||||
return parent_->GetHostContentSettingsMap();
|
||||
}
|
||||
|
||||
PrefService* CefBrowserContextProxy::GetPrefs() {
|
||||
return parent_->GetPrefs();
|
||||
}
|
||||
|
@@ -69,6 +69,8 @@ class CefBrowserContextProxy : public CefBrowserContext {
|
||||
content::ProtocolHandlerMap* protocol_handlers,
|
||||
content::URLRequestInterceptorScopedVector request_interceptors)
|
||||
override;
|
||||
HostContentSettingsMap* GetHostContentSettingsMap() override;
|
||||
PrefService* GetPrefs() override;
|
||||
|
||||
scoped_refptr<CefBrowserContextImpl> parent() const {
|
||||
return parent_;
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "base/command_line.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "chrome/browser/plugins/plugin_finder.h"
|
||||
#include "content/browser/webui/content_web_ui_controller_factory.h"
|
||||
#include "content/public/browser/child_process_security_policy.h"
|
||||
#include "content/public/browser/gpu_data_manager.h"
|
||||
@@ -165,6 +166,9 @@ void CefBrowserMainParts::PreMainMessageLoopRun() {
|
||||
}
|
||||
}
|
||||
|
||||
// Triggers initialization of the singleton instance on UI thread.
|
||||
PluginFinder::GetInstance()->Init();
|
||||
|
||||
#if defined(OS_WIN)
|
||||
PlatformPreMainMessageLoopRun();
|
||||
#endif
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include "chrome/browser/prefs/command_line_pref_store.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "components/content_settings/core/browser/host_content_settings_map.h"
|
||||
#include "components/pref_registry/pref_registry_syncable.h"
|
||||
#include "components/proxy_config/proxy_config_dictionary.h"
|
||||
#include "extensions/browser/extension_prefs.h"
|
||||
@@ -84,13 +85,14 @@ scoped_ptr<PrefService> CefBrowserPrefStore::CreateService() {
|
||||
new CommandLinePrefStore(command_line));
|
||||
factory.set_user_prefs(this);
|
||||
|
||||
scoped_refptr< user_prefs::PrefRegistrySyncable> registry(
|
||||
scoped_refptr<user_prefs::PrefRegistrySyncable> registry(
|
||||
new user_prefs::PrefRegistrySyncable());
|
||||
|
||||
// Default settings.
|
||||
CefMediaCaptureDevicesDispatcher::RegisterPrefs(registry.get());
|
||||
PrefProxyConfigTrackerImpl::RegisterPrefs(registry.get());
|
||||
extensions::ExtensionPrefs::RegisterProfilePrefs(registry.get());
|
||||
HostContentSettingsMap::RegisterProfilePrefs(registry.get());
|
||||
|
||||
// Print settings.
|
||||
registry->RegisterBooleanPref(prefs::kPrintingEnabled, true);
|
||||
@@ -122,6 +124,11 @@ scoped_ptr<PrefService> CefBrowserPrefStore::CreateService() {
|
||||
registry->RegisterBooleanPref(prefs::kEnableDRM, false);
|
||||
registry->RegisterStringPref(prefs::kDRMSalt, "");
|
||||
|
||||
// Plugin settings.
|
||||
// Based on chrome::RegisterBrowserUserPrefs.
|
||||
registry->RegisterBooleanPref(prefs::kPluginsAllowOutdated, false);
|
||||
registry->RegisterBooleanPref(prefs::kPluginsAlwaysAuthorize, false);
|
||||
|
||||
return factory.Create(registry.get());
|
||||
}
|
||||
|
||||
|
@@ -16,9 +16,9 @@
|
||||
#include "libcef/browser/devtools_delegate.h"
|
||||
#include "libcef/browser/extensions/browser_extensions_util.h"
|
||||
#include "libcef/browser/extensions/extension_system.h"
|
||||
#include "libcef/browser/extensions/plugin_info_message_filter.h"
|
||||
#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/printing/printing_message_filter.h"
|
||||
#include "libcef/browser/resource_dispatcher_host_delegate.h"
|
||||
#include "libcef/browser/speech_recognition_manager_delegate.h"
|
||||
@@ -38,14 +38,14 @@
|
||||
#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"
|
||||
#include "content/public/browser/child_process_security_policy.h"
|
||||
#include "content/public/browser/client_certificate_delegate.h"
|
||||
#include "content/public/browser/plugin_service_filter.h"
|
||||
#include "content/public/browser/quota_permission_context.h"
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/render_widget_host_view.h"
|
||||
#include "content/public/browser/resource_dispatcher_host.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
@@ -58,6 +58,7 @@
|
||||
#include "extensions/browser/io_thread_extension_message_filter.h"
|
||||
#include "extensions/common/constants.h"
|
||||
#include "net/ssl/ssl_cert_request_info.h"
|
||||
#include "ppapi/host/ppapi_host.h"
|
||||
#include "third_party/WebKit/public/web/WebWindowFeatures.h"
|
||||
#include "ui/base/ui_base_switches.h"
|
||||
#include "url/gurl.h"
|
||||
@@ -265,48 +266,6 @@ class CefQuotaPermissionContext : public content::QuotaPermissionContext {
|
||||
DISALLOW_COPY_AND_ASSIGN(CefQuotaPermissionContext);
|
||||
};
|
||||
|
||||
class CefPluginServiceFilter : public content::PluginServiceFilter {
|
||||
public:
|
||||
CefPluginServiceFilter() {}
|
||||
|
||||
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 allowed = true;
|
||||
|
||||
CefRefPtr<CefBrowserHostImpl> browser =
|
||||
CefBrowserHostImpl::GetBrowserForFrame(render_process_id,
|
||||
render_frame_id);
|
||||
if (browser.get()) {
|
||||
CefRefPtr<CefClient> client = browser->GetClient();
|
||||
if (client.get()) {
|
||||
CefRefPtr<CefRequestHandler> handler = client->GetRequestHandler();
|
||||
if (handler.get()) {
|
||||
CefRefPtr<CefWebPluginInfoImpl> pluginInfo(
|
||||
new CefWebPluginInfoImpl(*plugin));
|
||||
allowed =
|
||||
!handler->OnBeforePluginLoad(browser.get(),
|
||||
url.possibly_invalid_spec(),
|
||||
policy_url.possibly_invalid_spec(),
|
||||
pluginInfo.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allowed;
|
||||
}
|
||||
|
||||
bool CanLoadPlugin(int render_process_id,
|
||||
const base::FilePath& path) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CefPluginServiceFilter);
|
||||
};
|
||||
|
||||
void TranslatePopupFeatures(const blink::WebWindowFeatures& webKitFeatures,
|
||||
CefPopupFeatures& features) {
|
||||
features.x = static_cast<int>(webKitFeatures.x);
|
||||
@@ -399,10 +358,6 @@ 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;
|
||||
}
|
||||
|
||||
@@ -574,9 +529,10 @@ void CefContentBrowserClient::RenderProcessWillLaunch(
|
||||
|
||||
content::BrowserContext* browser_context = host->GetBrowserContext();
|
||||
|
||||
host->AddFilter(new CefPluginInfoMessageFilter(id,
|
||||
static_cast<CefBrowserContext*>(browser_context)));
|
||||
|
||||
if (extensions::ExtensionsEnabled()) {
|
||||
host->AddFilter(
|
||||
new extensions::CefPluginInfoMessageFilter(id, browser_context));
|
||||
host->AddFilter(
|
||||
new extensions::ExtensionMessageFilter(id, browser_context));
|
||||
host->AddFilter(
|
||||
|
@@ -32,7 +32,6 @@ class CefResourceDispatcherHostDelegate;
|
||||
class PrefService;
|
||||
|
||||
namespace content {
|
||||
class PluginServiceFilter;
|
||||
class SiteInstance;
|
||||
}
|
||||
|
||||
@@ -174,7 +173,6 @@ class CefContentBrowserClient : public content::ContentBrowserClient {
|
||||
|
||||
CefBrowserMainParts* browser_main_parts_;
|
||||
|
||||
scoped_ptr<content::PluginServiceFilter> plugin_service_filter_;
|
||||
scoped_ptr<CefResourceDispatcherHostDelegate>
|
||||
resource_dispatcher_host_delegate_;
|
||||
|
||||
|
@@ -133,3 +133,13 @@ CefContextMenuParamsImpl::EditStateFlags
|
||||
CEF_VALUE_VERIFY_RETURN(false, CM_EDITFLAG_NONE);
|
||||
return static_cast<EditStateFlags>(const_value().edit_flags);
|
||||
}
|
||||
|
||||
bool CefContextMenuParamsImpl::IsCustomMenu() {
|
||||
CEF_VALUE_VERIFY_RETURN(false, false);
|
||||
return !const_value().custom_items.empty();
|
||||
}
|
||||
|
||||
bool CefContextMenuParamsImpl::IsPepperMenu() {
|
||||
CEF_VALUE_VERIFY_RETURN(false, false);
|
||||
return const_value().custom_context.is_pepper_menu;
|
||||
}
|
||||
|
@@ -37,6 +37,8 @@ class CefContextMenuParamsImpl
|
||||
bool IsEditable() override;
|
||||
bool IsSpellCheckEnabled() override;
|
||||
EditStateFlags GetEditStateFlags() override;
|
||||
bool IsCustomMenu() override;
|
||||
bool IsPepperMenu() override;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CefContextMenuParamsImpl);
|
||||
};
|
||||
|
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "libcef/browser/extensions/extensions_browser_client.h"
|
||||
|
||||
#include "libcef/browser/content_browser_client.h"
|
||||
#include "libcef/browser/browser_context_impl.h"
|
||||
#include "libcef/browser/extensions/component_extension_resource_manager.h"
|
||||
#include "libcef/browser/extensions/extension_system_factory.h"
|
||||
#include "libcef/browser/extensions/extension_web_contents_observer.h"
|
||||
@@ -124,8 +124,7 @@ bool CefExtensionsBrowserClient::AllowCrossRendererResourceLoad(
|
||||
|
||||
PrefService* CefExtensionsBrowserClient::GetPrefServiceForContext(
|
||||
BrowserContext* context) {
|
||||
// TODO(extensions): Do we need a per-context PrefService?
|
||||
return CefContentBrowserClient::Get()->pref_service();
|
||||
return CefBrowserContextImpl::GetForContext(context)->GetPrefs();
|
||||
}
|
||||
|
||||
void CefExtensionsBrowserClient::GetEarlyExtensionPrefsObservers(
|
||||
|
@@ -1,245 +0,0 @@
|
||||
// Copyright 2015 The Chromium Embedded Framework Authors.
|
||||
// Portions copyright 2012 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 "libcef/browser/extensions/plugin_info_message_filter.h"
|
||||
|
||||
#include "libcef/common/cef_messages.h"
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/thread_task_runner_handle.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/plugin_service.h"
|
||||
#include "content/public/browser/plugin_service_filter.h"
|
||||
#include "content/public/common/content_constants.h"
|
||||
#include "content/public/common/webplugininfo.h"
|
||||
#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
|
||||
#include "extensions/common/constants.h"
|
||||
#include "extensions/common/extension.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
using content::PluginService;
|
||||
using content::WebPluginInfo;
|
||||
|
||||
namespace extensions {
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(OS_WIN) || defined(OS_MACOSX)
|
||||
// These are the mime-types of plugins which are known to have PPAPI versions.
|
||||
const char* kPepperPluginMimeTypes[] = {
|
||||
"application/pdf",
|
||||
"application/x-google-chrome-pdf",
|
||||
"application/x-nacl",
|
||||
"application/x-pnacl",
|
||||
"application/vnd.chromium.remoting-viewer",
|
||||
"application/x-shockwave-flash",
|
||||
"application/futuresplash",
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
CefPluginInfoMessageFilter::Context::Context(
|
||||
int render_process_id,
|
||||
content::BrowserContext* browser_context)
|
||||
: render_process_id_(render_process_id),
|
||||
resource_context_(browser_context->GetResourceContext()) {
|
||||
}
|
||||
|
||||
CefPluginInfoMessageFilter::Context::~Context() {
|
||||
}
|
||||
|
||||
CefPluginInfoMessageFilter::CefPluginInfoMessageFilter(
|
||||
int render_process_id,
|
||||
content::BrowserContext* browser_context)
|
||||
: BrowserMessageFilter(ExtensionMsgStart),
|
||||
context_(render_process_id, browser_context),
|
||||
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
||||
weak_ptr_factory_(this) {
|
||||
}
|
||||
|
||||
bool CefPluginInfoMessageFilter::OnMessageReceived(const IPC::Message& message) {
|
||||
IPC_BEGIN_MESSAGE_MAP(CefPluginInfoMessageFilter, message)
|
||||
IPC_MESSAGE_HANDLER_DELAY_REPLY(CefViewHostMsg_GetPluginInfo,
|
||||
OnGetPluginInfo)
|
||||
IPC_MESSAGE_HANDLER(CefViewHostMsg_IsInternalPluginAvailableForMimeType,
|
||||
OnIsInternalPluginAvailableForMimeType)
|
||||
IPC_MESSAGE_UNHANDLED(return false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
return true;
|
||||
}
|
||||
|
||||
void CefPluginInfoMessageFilter::OnDestruct() const {
|
||||
const_cast<CefPluginInfoMessageFilter*>(this)->
|
||||
weak_ptr_factory_.InvalidateWeakPtrs();
|
||||
|
||||
// Destroy on the UI thread because we contain a |PrefMember|.
|
||||
content::BrowserThread::DeleteOnUIThread::Destruct(this);
|
||||
}
|
||||
|
||||
CefPluginInfoMessageFilter::~CefPluginInfoMessageFilter() {}
|
||||
|
||||
struct CefPluginInfoMessageFilter::GetPluginInfo_Params {
|
||||
int render_frame_id;
|
||||
GURL url;
|
||||
GURL top_origin_url;
|
||||
std::string mime_type;
|
||||
};
|
||||
|
||||
void CefPluginInfoMessageFilter::OnGetPluginInfo(
|
||||
int render_frame_id,
|
||||
const GURL& url,
|
||||
const GURL& top_origin_url,
|
||||
const std::string& mime_type,
|
||||
IPC::Message* reply_msg) {
|
||||
GetPluginInfo_Params params = {
|
||||
render_frame_id,
|
||||
url,
|
||||
top_origin_url,
|
||||
mime_type
|
||||
};
|
||||
PluginService::GetInstance()->GetPlugins(
|
||||
base::Bind(&CefPluginInfoMessageFilter::PluginsLoaded,
|
||||
weak_ptr_factory_.GetWeakPtr(),
|
||||
params, reply_msg));
|
||||
}
|
||||
|
||||
void CefPluginInfoMessageFilter::PluginsLoaded(
|
||||
const GetPluginInfo_Params& params,
|
||||
IPC::Message* reply_msg,
|
||||
const std::vector<WebPluginInfo>& plugins) {
|
||||
CefViewHostMsg_GetPluginInfo_Output output;
|
||||
// This also fills in |actual_mime_type|.
|
||||
if (context_.FindEnabledPlugin(params.render_frame_id, params.url,
|
||||
params.top_origin_url, params.mime_type,
|
||||
&output.status, &output.plugin,
|
||||
&output.actual_mime_type)) {
|
||||
context_.DecidePluginStatus(params, output.plugin, &output.status);
|
||||
}
|
||||
|
||||
CefViewHostMsg_GetPluginInfo::WriteReplyParams(reply_msg, output);
|
||||
Send(reply_msg);
|
||||
}
|
||||
|
||||
void CefPluginInfoMessageFilter::Context::DecidePluginStatus(
|
||||
const GetPluginInfo_Params& params,
|
||||
const WebPluginInfo& plugin,
|
||||
CefViewHostMsg_GetPluginInfo_Status* status) const {
|
||||
if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) {
|
||||
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
// NPAPI plugins are not supported inside <webview> guests.
|
||||
if (extensions::WebViewRendererState::GetInstance()->IsGuest(
|
||||
render_process_id_)) {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the plugin is crashing too much.
|
||||
if (PluginService::GetInstance()->IsPluginUnstable(plugin.path)) {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kUnauthorized;
|
||||
return;
|
||||
}
|
||||
|
||||
if (*status == CefViewHostMsg_GetPluginInfo_Status::kAllowed) {
|
||||
// Allow an embedder of <webview> to block a plugin from being loaded inside
|
||||
// the guest. In order to do this, set the status to 'Unauthorized' here,
|
||||
// and update the status as appropriate depending on the response from the
|
||||
// embedder.
|
||||
if (extensions::WebViewRendererState::GetInstance()->IsGuest(
|
||||
render_process_id_)) {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kUnauthorized;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CefPluginInfoMessageFilter::Context::FindEnabledPlugin(
|
||||
int render_frame_id,
|
||||
const GURL& url,
|
||||
const GURL& top_origin_url,
|
||||
const std::string& mime_type,
|
||||
CefViewHostMsg_GetPluginInfo_Status* status,
|
||||
WebPluginInfo* plugin,
|
||||
std::string* actual_mime_type) const {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kAllowed;
|
||||
|
||||
bool allow_wildcard = true;
|
||||
std::vector<WebPluginInfo> matching_plugins;
|
||||
std::vector<std::string> mime_types;
|
||||
PluginService::GetInstance()->GetPluginInfoArray(
|
||||
url, mime_type, allow_wildcard, &matching_plugins, &mime_types);
|
||||
if (matching_plugins.empty()) {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kNotFound;
|
||||
#if defined(OS_WIN) || defined(OS_MACOSX)
|
||||
if (!PluginService::GetInstance()->NPAPIPluginsSupported()) {
|
||||
// At this point it is not known for sure this is an NPAPI plugin as it
|
||||
// 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)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
content::PluginServiceFilter* filter =
|
||||
PluginService::GetInstance()->GetFilter();
|
||||
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])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void CefPluginInfoMessageFilter::OnIsInternalPluginAvailableForMimeType(
|
||||
const std::string& mime_type,
|
||||
bool* is_available,
|
||||
std::vector<base::string16>* additional_param_names,
|
||||
std::vector<base::string16>* additional_param_values) {
|
||||
std::vector<WebPluginInfo> plugins;
|
||||
PluginService::GetInstance()->GetInternalPlugins(&plugins);
|
||||
|
||||
for (size_t i = 0; i < plugins.size(); ++i) {
|
||||
const WebPluginInfo& plugin = plugins[i];
|
||||
const std::vector<content::WebPluginMimeType>& mime_types =
|
||||
plugin.mime_types;
|
||||
for (size_t j = 0; j < mime_types.size(); ++j) {
|
||||
if (mime_types[j].mime_type == mime_type) {
|
||||
// TODO(cef): Maybe allow plugins to be disabled here.
|
||||
*is_available = true;
|
||||
*additional_param_names = mime_types[j].additional_param_names;
|
||||
*additional_param_values = mime_types[j].additional_param_values;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*is_available = false;
|
||||
}
|
||||
|
||||
} // namespace extensions
|
@@ -5,6 +5,7 @@
|
||||
#include "libcef/browser/menu_creator.h"
|
||||
#include "libcef/browser/browser_host_impl.h"
|
||||
#include "libcef/browser/context_menu_params_impl.h"
|
||||
#include "libcef/browser/thread_util.h"
|
||||
#include "libcef/common/content_client.h"
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
@@ -31,12 +32,73 @@ CefString GetLabel(int message_id) {
|
||||
return label;
|
||||
}
|
||||
|
||||
const int kInvalidCommandId = -1;
|
||||
const cef_event_flags_t kEmptyEventFlags = static_cast<cef_event_flags_t>(0);
|
||||
|
||||
class CefRunContextMenuCallbackImpl : public CefRunContextMenuCallback {
|
||||
public:
|
||||
typedef base::Callback<void(int,cef_event_flags_t)> Callback;
|
||||
|
||||
explicit CefRunContextMenuCallbackImpl(const Callback& callback)
|
||||
: callback_(callback) {
|
||||
}
|
||||
|
||||
~CefRunContextMenuCallbackImpl() {
|
||||
if (!callback_.is_null()) {
|
||||
// The callback is still pending. Cancel it now.
|
||||
if (CEF_CURRENTLY_ON_UIT()) {
|
||||
RunNow(callback_, kInvalidCommandId, kEmptyEventFlags);
|
||||
} else {
|
||||
CEF_POST_TASK(CEF_UIT,
|
||||
base::Bind(&CefRunContextMenuCallbackImpl::RunNow, callback_,
|
||||
kInvalidCommandId, kEmptyEventFlags));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Continue(int command_id, cef_event_flags_t event_flags) override {
|
||||
if (CEF_CURRENTLY_ON_UIT()) {
|
||||
if (!callback_.is_null()) {
|
||||
RunNow(callback_, command_id, event_flags);
|
||||
callback_.Reset();
|
||||
}
|
||||
} else {
|
||||
CEF_POST_TASK(CEF_UIT,
|
||||
base::Bind(&CefRunContextMenuCallbackImpl::Continue, this,
|
||||
command_id, event_flags));
|
||||
}
|
||||
}
|
||||
|
||||
void Cancel() override {
|
||||
Continue(kInvalidCommandId, kEmptyEventFlags);
|
||||
}
|
||||
|
||||
void Disconnect() {
|
||||
callback_.Reset();
|
||||
}
|
||||
|
||||
private:
|
||||
static void RunNow(const Callback& callback,
|
||||
int command_id,
|
||||
cef_event_flags_t event_flags) {
|
||||
CEF_REQUIRE_UIT();
|
||||
callback.Run(command_id, event_flags);
|
||||
}
|
||||
|
||||
Callback callback_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(CefRunContextMenuCallbackImpl);
|
||||
DISALLOW_COPY_AND_ASSIGN(CefRunContextMenuCallbackImpl);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
CefMenuCreator::CefMenuCreator(content::WebContents* web_contents,
|
||||
CefBrowserHostImpl* browser)
|
||||
: content::WebContentsObserver(web_contents),
|
||||
browser_(browser) {
|
||||
browser_(browser),
|
||||
custom_menu_callback_(NULL),
|
||||
weak_ptr_factory_(this) {
|
||||
DCHECK(web_contents);
|
||||
DCHECK(browser_);
|
||||
model_ = new CefMenuModelImpl(this);
|
||||
@@ -77,39 +139,75 @@ bool CefMenuCreator::CreateContextMenu(
|
||||
// Create the default menu model.
|
||||
CreateDefaultModel();
|
||||
|
||||
bool custom_menu = false;
|
||||
DCHECK(!custom_menu_callback_);
|
||||
|
||||
// Give the client a chance to modify the model.
|
||||
CefRefPtr<CefClient> client = browser_->GetClient();
|
||||
if (client.get()) {
|
||||
CefRefPtr<CefContextMenuHandler> handler =
|
||||
client->GetContextMenuHandler();
|
||||
if (handler.get()) {
|
||||
CefRefPtr<CefContextMenuParamsImpl> paramsPtr(
|
||||
new CefContextMenuParamsImpl(¶ms_));
|
||||
CefRefPtr<CefContextMenuHandler> handler =
|
||||
client->GetContextMenuHandler();
|
||||
if (handler.get()) {
|
||||
CefRefPtr<CefContextMenuParamsImpl> paramsPtr(
|
||||
new CefContextMenuParamsImpl(¶ms_));
|
||||
CefRefPtr<CefFrame> frame = browser_->GetFocusedFrame();
|
||||
|
||||
handler->OnBeforeContextMenu(browser_,
|
||||
browser_->GetFocusedFrame(),
|
||||
paramsPtr.get(),
|
||||
model_.get());
|
||||
handler->OnBeforeContextMenu(browser_,
|
||||
frame,
|
||||
paramsPtr.get(),
|
||||
model_.get());
|
||||
|
||||
// Do not keep references to the parameters in the callback.
|
||||
paramsPtr->Detach(NULL);
|
||||
DCHECK(paramsPtr->HasOneRef());
|
||||
DCHECK(model_->VerifyRefCount());
|
||||
MenuWillShow(model_);
|
||||
|
||||
// Menu is empty so notify the client and return.
|
||||
if (model_->GetCount() == 0) {
|
||||
MenuClosed(model_);
|
||||
return true;
|
||||
if (model_->GetCount() > 0) {
|
||||
CefRefPtr<CefRunContextMenuCallbackImpl> callbackImpl(
|
||||
new CefRunContextMenuCallbackImpl(
|
||||
base::Bind(&CefMenuCreator::ExecuteCommandCallback,
|
||||
weak_ptr_factory_.GetWeakPtr())));
|
||||
|
||||
// This reference will be cleared when the callback is executed or
|
||||
// the callback object is deleted.
|
||||
custom_menu_callback_ = callbackImpl.get();
|
||||
|
||||
if (handler->RunContextMenu(browser_,
|
||||
frame,
|
||||
paramsPtr.get(),
|
||||
model_.get(),
|
||||
callbackImpl.get())) {
|
||||
custom_menu = true;
|
||||
} else {
|
||||
// Callback should not be executed if the handler returns false.
|
||||
DCHECK(custom_menu_callback_);
|
||||
custom_menu_callback_ = NULL;
|
||||
callbackImpl->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Do not keep references to the parameters in the callback.
|
||||
paramsPtr->Detach(NULL);
|
||||
DCHECK(paramsPtr->HasOneRef());
|
||||
DCHECK(model_->VerifyRefCount());
|
||||
|
||||
// Menu is empty so notify the client and return.
|
||||
if (model_->GetCount() == 0 && !custom_menu) {
|
||||
MenuClosed(model_);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (custom_menu)
|
||||
return true;
|
||||
return runner_->RunContextMenu(this);
|
||||
}
|
||||
|
||||
void CefMenuCreator::CancelContextMenu() {
|
||||
if (IsShowingContextMenu())
|
||||
runner_->CancelContextMenu();
|
||||
if (IsShowingContextMenu()) {
|
||||
if (custom_menu_callback_)
|
||||
custom_menu_callback_->Cancel();
|
||||
else
|
||||
runner_->CancelContextMenu();
|
||||
}
|
||||
}
|
||||
|
||||
bool CefMenuCreator::CreateRunner() {
|
||||
@@ -135,26 +233,26 @@ void CefMenuCreator::ExecuteCommand(CefRefPtr<CefMenuModelImpl> source,
|
||||
// Give the client a chance to handle the command.
|
||||
CefRefPtr<CefClient> client = browser_->GetClient();
|
||||
if (client.get()) {
|
||||
CefRefPtr<CefContextMenuHandler> handler =
|
||||
client->GetContextMenuHandler();
|
||||
if (handler.get()) {
|
||||
CefRefPtr<CefContextMenuParamsImpl> paramsPtr(
|
||||
new CefContextMenuParamsImpl(¶ms_));
|
||||
CefRefPtr<CefContextMenuHandler> handler =
|
||||
client->GetContextMenuHandler();
|
||||
if (handler.get()) {
|
||||
CefRefPtr<CefContextMenuParamsImpl> paramsPtr(
|
||||
new CefContextMenuParamsImpl(¶ms_));
|
||||
|
||||
bool handled = handler->OnContextMenuCommand(
|
||||
browser_,
|
||||
browser_->GetFocusedFrame(),
|
||||
paramsPtr.get(),
|
||||
command_id,
|
||||
event_flags);
|
||||
bool handled = handler->OnContextMenuCommand(
|
||||
browser_,
|
||||
browser_->GetFocusedFrame(),
|
||||
paramsPtr.get(),
|
||||
command_id,
|
||||
event_flags);
|
||||
|
||||
// Do not keep references to the parameters in the callback.
|
||||
paramsPtr->Detach(NULL);
|
||||
DCHECK(paramsPtr->HasOneRef());
|
||||
// Do not keep references to the parameters in the callback.
|
||||
paramsPtr->Detach(NULL);
|
||||
DCHECK(paramsPtr->HasOneRef());
|
||||
|
||||
if (handled)
|
||||
return;
|
||||
}
|
||||
if (handled)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the default command handling.
|
||||
@@ -169,6 +267,10 @@ void CefMenuCreator::MenuWillShow(CefRefPtr<CefMenuModelImpl> source) {
|
||||
if (!web_contents())
|
||||
return;
|
||||
|
||||
// May be called multiple times.
|
||||
if (IsShowingContextMenu())
|
||||
return;
|
||||
|
||||
// Notify the host before showing the context menu.
|
||||
content::RenderWidgetHostView* view =
|
||||
web_contents()->GetRenderWidgetHostView();
|
||||
@@ -181,31 +283,56 @@ void CefMenuCreator::MenuClosed(CefRefPtr<CefMenuModelImpl> source) {
|
||||
if (source.get() != model_.get())
|
||||
return;
|
||||
|
||||
if (!web_contents())
|
||||
return;
|
||||
|
||||
DCHECK(IsShowingContextMenu());
|
||||
|
||||
// Notify the client.
|
||||
CefRefPtr<CefClient> client = browser_->GetClient();
|
||||
if (client.get()) {
|
||||
CefRefPtr<CefContextMenuHandler> handler =
|
||||
client->GetContextMenuHandler();
|
||||
if (handler.get()) {
|
||||
handler->OnContextMenuDismissed(browser_, browser_->GetFocusedFrame());
|
||||
}
|
||||
CefRefPtr<CefContextMenuHandler> handler =
|
||||
client->GetContextMenuHandler();
|
||||
if (handler.get()) {
|
||||
handler->OnContextMenuDismissed(browser_, browser_->GetFocusedFrame());
|
||||
}
|
||||
}
|
||||
|
||||
if (IsShowingContextMenu() && web_contents()) {
|
||||
// Notify the host after closing the context menu.
|
||||
content::RenderWidgetHostView* view =
|
||||
web_contents()->GetRenderWidgetHostView();
|
||||
if (view)
|
||||
view->SetShowingContextMenu(false);
|
||||
web_contents()->NotifyContextMenuClosed(params_.custom_context);
|
||||
}
|
||||
// Notify the host after closing the context menu.
|
||||
content::RenderWidgetHostView* view =
|
||||
web_contents()->GetRenderWidgetHostView();
|
||||
if (view)
|
||||
view->SetShowingContextMenu(false);
|
||||
web_contents()->NotifyContextMenuClosed(params_.custom_context);
|
||||
}
|
||||
|
||||
bool CefMenuCreator::FormatLabel(base::string16& label) {
|
||||
return runner_->FormatLabel(label);
|
||||
}
|
||||
|
||||
void CefMenuCreator::ExecuteCommandCallback(int command_id,
|
||||
cef_event_flags_t event_flags) {
|
||||
DCHECK(IsShowingContextMenu());
|
||||
DCHECK(custom_menu_callback_);
|
||||
if (command_id != kInvalidCommandId)
|
||||
ExecuteCommand(model_, command_id, event_flags);
|
||||
MenuClosed(model_);
|
||||
custom_menu_callback_ = NULL;
|
||||
}
|
||||
|
||||
void CefMenuCreator::CreateDefaultModel() {
|
||||
if (!params_.custom_items.empty()) {
|
||||
// Custom menu items originating from the renderer process. For example,
|
||||
// plugin placeholder menu items or Flash menu items.
|
||||
for (size_t i = 0; i < params_.custom_items.size(); ++i) {
|
||||
content::MenuItem menu_item = params_.custom_items[i];
|
||||
menu_item.action += MENU_ID_CUSTOM_FIRST;
|
||||
DCHECK_LE(static_cast<int>(menu_item.action), MENU_ID_CUSTOM_LAST);
|
||||
model_->AddMenuItem(menu_item);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (params_.is_editable) {
|
||||
// Editable node.
|
||||
model_->AddItem(MENU_ID_UNDO, GetLabel(IDS_MENU_UNDO));
|
||||
@@ -284,6 +411,14 @@ void CefMenuCreator::CreateDefaultModel() {
|
||||
}
|
||||
|
||||
void CefMenuCreator::ExecuteDefaultCommand(int command_id) {
|
||||
if (IsCustomContextMenuCommand(command_id)) {
|
||||
if (web_contents()) {
|
||||
web_contents()->ExecuteCustomContextMenuCommand(
|
||||
command_id - MENU_ID_CUSTOM_FIRST, params_.custom_context);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user chose a replacement word for a misspelling, replace it here.
|
||||
if (command_id >= MENU_ID_SPELLCHECK_SUGGESTION_0 &&
|
||||
command_id <= MENU_ID_SPELLCHECK_SUGGESTION_LAST) {
|
||||
@@ -358,3 +493,20 @@ void CefMenuCreator::ExecuteDefaultCommand(int command_id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool CefMenuCreator::IsCustomContextMenuCommand(int command_id) {
|
||||
// Verify that the command ID is in the correct range.
|
||||
if (command_id < MENU_ID_CUSTOM_FIRST || command_id > MENU_ID_CUSTOM_LAST)
|
||||
return false;
|
||||
|
||||
command_id -= MENU_ID_CUSTOM_FIRST;
|
||||
|
||||
// Verify that the specific command ID was passed from the renderer process.
|
||||
if (!params_.custom_items.empty()) {
|
||||
for (size_t i = 0; i < params_.custom_items.size(); ++i) {
|
||||
if (static_cast<int>(params_.custom_items[i].action) == command_id)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include "libcef/browser/menu_model_impl.h"
|
||||
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "content/public/common/context_menu_params.h"
|
||||
|
||||
@@ -17,6 +18,7 @@ class RenderFrameHost;
|
||||
class WebContents;
|
||||
};
|
||||
|
||||
class CefRunContextMenuCallback;
|
||||
class CefBrowserHostImpl;
|
||||
|
||||
class CefMenuCreator : public CefMenuModelImpl::Delegate,
|
||||
@@ -58,11 +60,17 @@ class CefMenuCreator : public CefMenuModelImpl::Delegate,
|
||||
void MenuClosed(CefRefPtr<CefMenuModelImpl> source) override;
|
||||
bool FormatLabel(base::string16& label) override;
|
||||
|
||||
void ExecuteCommandCallback(int command_id,
|
||||
cef_event_flags_t event_flags);
|
||||
|
||||
// Create the default menu model.
|
||||
void CreateDefaultModel();
|
||||
// Execute the default command handling.
|
||||
void ExecuteDefaultCommand(int command_id);
|
||||
|
||||
// Returns true if the specified id is a custom context menu command.
|
||||
bool IsCustomContextMenuCommand(int command_id);
|
||||
|
||||
// CefBrowserHostImpl pointer is guaranteed to outlive this object.
|
||||
CefBrowserHostImpl* browser_;
|
||||
|
||||
@@ -70,6 +78,12 @@ class CefMenuCreator : public CefMenuModelImpl::Delegate,
|
||||
content::ContextMenuParams params_;
|
||||
scoped_ptr<Runner> runner_;
|
||||
|
||||
// Not owned by this class.
|
||||
CefRunContextMenuCallback* custom_menu_callback_;
|
||||
|
||||
// Must be the last member.
|
||||
base::WeakPtrFactory<CefMenuCreator> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CefMenuCreator);
|
||||
};
|
||||
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#include "base/bind.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "content/public/common/menu_item.h"
|
||||
#include "ui/base/accelerators/accelerator.h"
|
||||
|
||||
namespace {
|
||||
@@ -661,6 +662,41 @@ bool CefMenuModelImpl::VerifyRefCount() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void CefMenuModelImpl::AddMenuItem(const content::MenuItem& menu_item) {
|
||||
const int command_id = static_cast<int>(menu_item.action);
|
||||
|
||||
switch (menu_item.type) {
|
||||
case content::MenuItem::OPTION:
|
||||
AddItem(command_id, menu_item.label);
|
||||
break;
|
||||
case content::MenuItem::CHECKABLE_OPTION:
|
||||
AddCheckItem(command_id, menu_item.label);
|
||||
break;
|
||||
case content::MenuItem::GROUP:
|
||||
AddRadioItem(command_id, menu_item.label, 0);
|
||||
break;
|
||||
case content::MenuItem::SEPARATOR:
|
||||
AddSeparator();
|
||||
break;
|
||||
case content::MenuItem::SUBMENU: {
|
||||
CefRefPtr<CefMenuModelImpl> sub_menu = static_cast<CefMenuModelImpl*>(
|
||||
AddSubMenu(command_id, menu_item.label).get());
|
||||
for (size_t i = 0; i < menu_item.submenu.size(); ++i)
|
||||
sub_menu->AddMenuItem(menu_item.submenu[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!menu_item.enabled && menu_item.type != content::MenuItem::SEPARATOR)
|
||||
SetEnabled(command_id, false);
|
||||
|
||||
if (menu_item.checked &&
|
||||
(menu_item.type == content::MenuItem::CHECKABLE_OPTION ||
|
||||
menu_item.type == content::MenuItem::GROUP)) {
|
||||
SetChecked(command_id, true);
|
||||
}
|
||||
}
|
||||
|
||||
void CefMenuModelImpl::AppendItem(const Item& item) {
|
||||
ValidateItem(item);
|
||||
items_.push_back(item);
|
||||
|
@@ -15,6 +15,10 @@
|
||||
#include "base/threading/platform_thread.h"
|
||||
#include "ui/base/models/menu_model.h"
|
||||
|
||||
namespace content {
|
||||
struct MenuItem;
|
||||
}
|
||||
|
||||
class CefMenuModelImpl : public CefMenuModel {
|
||||
public:
|
||||
class Delegate {
|
||||
@@ -112,6 +116,9 @@ class CefMenuModelImpl : public CefMenuModel {
|
||||
// Verify that only a single reference exists to all CefMenuModelImpl objects.
|
||||
bool VerifyRefCount();
|
||||
|
||||
// Helper for adding custom menu items originating from the renderer process.
|
||||
void AddMenuItem(const content::MenuItem& menu_item);
|
||||
|
||||
ui::MenuModel* model() { return model_.get(); }
|
||||
Delegate* delegate() { return delegate_; }
|
||||
void set_delegate(Delegate* delegate) { delegate_ = NULL; }
|
||||
|
615
libcef/browser/plugins/plugin_info_message_filter.cc
Normal file
615
libcef/browser/plugins/plugin_info_message_filter.cc
Normal file
@@ -0,0 +1,615 @@
|
||||
// Copyright 2015 The Chromium Embedded Framework Authors.
|
||||
// Portions copyright 2012 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 "libcef/browser/plugins/plugin_info_message_filter.h"
|
||||
|
||||
#include "libcef/browser/browser_context.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"
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/thread_task_runner_handle.h"
|
||||
#include "chrome/browser/plugins/plugin_finder.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "components/content_settings/core/browser/content_settings_utils.h"
|
||||
#include "components/content_settings/core/browser/plugins_field_trial.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/plugin_service.h"
|
||||
#include "content/public/browser/plugin_service_filter.h"
|
||||
#include "content/public/common/content_constants.h"
|
||||
#include "content/public/common/webplugininfo.h"
|
||||
#include "extensions/browser/extension_registry.h"
|
||||
#include "extensions/browser/guest_view/web_view/web_view_renderer_state.h"
|
||||
#include "extensions/common/constants.h"
|
||||
#include "extensions/common/extension.h"
|
||||
#include "extensions/common/manifest_constants.h"
|
||||
#include "extensions/common/manifest_handlers/webview_info.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
#include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/win/metro.h"
|
||||
#endif
|
||||
|
||||
#if !defined(DISABLE_NACL)
|
||||
#include "components/nacl/common/nacl_constants.h"
|
||||
#endif
|
||||
|
||||
using content::PluginService;
|
||||
using content::WebPluginInfo;
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(OS_WIN) || defined(OS_MACOSX)
|
||||
// These are the mime-types of plugins which are known to have PPAPI versions.
|
||||
const char* kPepperPluginMimeTypes[] = {
|
||||
"application/pdf",
|
||||
"application/x-google-chrome-pdf",
|
||||
"application/x-nacl",
|
||||
"application/x-pnacl",
|
||||
"application/vnd.chromium.remoting-viewer",
|
||||
"application/x-shockwave-flash",
|
||||
"application/futuresplash",
|
||||
};
|
||||
#endif
|
||||
|
||||
// For certain sandboxed Pepper plugins, use the JavaScript Content Settings.
|
||||
bool ShouldUseJavaScriptSettingForPlugin(const WebPluginInfo& plugin) {
|
||||
if (plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS &&
|
||||
plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(DISABLE_NACL)
|
||||
// Treat Native Client invocations like JavaScript.
|
||||
if (plugin.name == base::ASCIIToUTF16(nacl::kNaClPluginName))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#if defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
|
||||
// Treat CDM invocations like JavaScript.
|
||||
if (plugin.name == base::ASCIIToUTF16(kWidevineCdmDisplayName)) {
|
||||
DCHECK(plugin.type == WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS);
|
||||
return true;
|
||||
}
|
||||
#endif // defined(WIDEVINE_CDM_AVAILABLE) && defined(ENABLE_PEPPER_CDMS)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_PEPPER_CDMS)
|
||||
|
||||
enum PluginAvailabilityStatusForUMA {
|
||||
PLUGIN_NOT_REGISTERED,
|
||||
PLUGIN_AVAILABLE,
|
||||
PLUGIN_DISABLED,
|
||||
PLUGIN_AVAILABILITY_STATUS_MAX
|
||||
};
|
||||
|
||||
static void SendPluginAvailabilityUMA(const std::string& mime_type,
|
||||
PluginAvailabilityStatusForUMA status) {
|
||||
#if defined(WIDEVINE_CDM_AVAILABLE)
|
||||
// Only report results for Widevine CDM.
|
||||
if (mime_type != kWidevineCdmPluginMimeType)
|
||||
return;
|
||||
|
||||
UMA_HISTOGRAM_ENUMERATION("Plugin.AvailabilityStatus.WidevineCdm",
|
||||
status, PLUGIN_AVAILABILITY_STATUS_MAX);
|
||||
#endif // defined(WIDEVINE_CDM_AVAILABLE)
|
||||
}
|
||||
|
||||
#endif // defined(ENABLE_PEPPER_CDMS)
|
||||
|
||||
void ReportMetrics(const std::string& mime_type,
|
||||
const GURL& url,
|
||||
const GURL& origin_url) {
|
||||
}
|
||||
|
||||
#if defined(ENABLE_EXTENSIONS)
|
||||
// Returns whether a request from a plugin to load |resource| from a renderer
|
||||
// with process id |process_id| is a request for an internal resource by an app
|
||||
// listed in |accessible_resources| in its manifest.
|
||||
bool IsPluginLoadingAccessibleResourceInWebView(
|
||||
extensions::ExtensionRegistry* extension_registry,
|
||||
int process_id,
|
||||
const GURL& resource) {
|
||||
extensions::WebViewRendererState* renderer_state =
|
||||
extensions::WebViewRendererState::GetInstance();
|
||||
std::string partition_id;
|
||||
if (!renderer_state->IsGuest(process_id) ||
|
||||
!renderer_state->GetPartitionID(process_id, &partition_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string extension_id = resource.host();
|
||||
const extensions::Extension* extension =
|
||||
extension_registry->GetExtensionById(extension_id,
|
||||
extensions::ExtensionRegistry::ENABLED);
|
||||
if (!extension)
|
||||
return false;
|
||||
const extensions::WebviewInfo* webview_info =
|
||||
static_cast<const extensions::WebviewInfo*>(extension->GetManifestData(
|
||||
extensions::manifest_keys::kWebviewAccessibleResources));
|
||||
if (!webview_info ||
|
||||
!webview_info->IsResourceWebviewAccessible(extension, partition_id,
|
||||
resource.path())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the renderer making the request actually belongs to the
|
||||
// same extension.
|
||||
std::string owner_extension;
|
||||
return renderer_state->GetOwnerInfo(process_id, nullptr, &owner_extension) &&
|
||||
owner_extension == extension_id;
|
||||
}
|
||||
#endif // defined(ENABLE_EXTENSIONS)
|
||||
|
||||
} // namespace
|
||||
|
||||
CefPluginInfoMessageFilter::Context::Context(
|
||||
int render_process_id,
|
||||
CefBrowserContext* profile)
|
||||
: render_process_id_(render_process_id),
|
||||
resource_context_(profile->GetResourceContext()),
|
||||
#if defined(ENABLE_EXTENSIONS)
|
||||
extension_registry_(extensions::ExtensionRegistry::Get(profile)),
|
||||
#endif
|
||||
host_content_settings_map_(profile->GetHostContentSettingsMap()) {
|
||||
allow_outdated_plugins_.Init(prefs::kPluginsAllowOutdated,
|
||||
profile->GetPrefs());
|
||||
allow_outdated_plugins_.MoveToThread(
|
||||
content::BrowserThread::GetMessageLoopProxyForThread(
|
||||
content::BrowserThread::IO));
|
||||
always_authorize_plugins_.Init(prefs::kPluginsAlwaysAuthorize,
|
||||
profile->GetPrefs());
|
||||
always_authorize_plugins_.MoveToThread(
|
||||
content::BrowserThread::GetMessageLoopProxyForThread(
|
||||
content::BrowserThread::IO));
|
||||
}
|
||||
|
||||
CefPluginInfoMessageFilter::Context::~Context() {
|
||||
}
|
||||
|
||||
CefPluginInfoMessageFilter::CefPluginInfoMessageFilter(
|
||||
int render_process_id,
|
||||
CefBrowserContext* profile)
|
||||
: BrowserMessageFilter(ExtensionMsgStart),
|
||||
context_(render_process_id, profile),
|
||||
browser_context_(profile),
|
||||
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
||||
weak_ptr_factory_(this) {
|
||||
}
|
||||
|
||||
bool CefPluginInfoMessageFilter::OnMessageReceived(
|
||||
const IPC::Message& message) {
|
||||
IPC_BEGIN_MESSAGE_MAP(CefPluginInfoMessageFilter, message)
|
||||
IPC_MESSAGE_HANDLER_DELAY_REPLY(CefViewHostMsg_GetPluginInfo,
|
||||
OnGetPluginInfo)
|
||||
#if defined(ENABLE_PEPPER_CDMS)
|
||||
IPC_MESSAGE_HANDLER(
|
||||
CefViewHostMsg_IsInternalPluginAvailableForMimeType,
|
||||
OnIsInternalPluginAvailableForMimeType)
|
||||
#endif
|
||||
IPC_MESSAGE_UNHANDLED(return false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
return true;
|
||||
}
|
||||
|
||||
void CefPluginInfoMessageFilter::OnDestruct() const {
|
||||
const_cast<CefPluginInfoMessageFilter*>(this)->
|
||||
weak_ptr_factory_.InvalidateWeakPtrs();
|
||||
|
||||
// Destroy on the UI thread because we contain a |PrefMember|.
|
||||
content::BrowserThread::DeleteOnUIThread::Destruct(this);
|
||||
}
|
||||
|
||||
CefPluginInfoMessageFilter::~CefPluginInfoMessageFilter() {}
|
||||
|
||||
struct CefPluginInfoMessageFilter::GetPluginInfo_Params {
|
||||
int render_frame_id;
|
||||
GURL url;
|
||||
GURL top_origin_url;
|
||||
std::string mime_type;
|
||||
};
|
||||
|
||||
void CefPluginInfoMessageFilter::OnGetPluginInfo(
|
||||
int render_frame_id,
|
||||
const GURL& url,
|
||||
const GURL& top_origin_url,
|
||||
const std::string& mime_type,
|
||||
IPC::Message* reply_msg) {
|
||||
GetPluginInfo_Params params = {
|
||||
render_frame_id,
|
||||
url,
|
||||
top_origin_url,
|
||||
mime_type
|
||||
};
|
||||
PluginService::GetInstance()->GetPlugins(
|
||||
base::Bind(&CefPluginInfoMessageFilter::PluginsLoaded,
|
||||
weak_ptr_factory_.GetWeakPtr(),
|
||||
params, reply_msg));
|
||||
}
|
||||
|
||||
void CefPluginInfoMessageFilter::PluginsLoaded(
|
||||
const GetPluginInfo_Params& params,
|
||||
IPC::Message* reply_msg,
|
||||
const std::vector<WebPluginInfo>& plugins) {
|
||||
CefViewHostMsg_GetPluginInfo_Output output;
|
||||
// This also fills in |actual_mime_type|.
|
||||
scoped_ptr<PluginMetadata> 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<CefRequestContextHandler> 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<CefWebPluginInfoImpl> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin_metadata) {
|
||||
output.group_identifier = plugin_metadata->identifier();
|
||||
output.group_name = plugin_metadata->name();
|
||||
}
|
||||
|
||||
CefViewHostMsg_GetPluginInfo::WriteReplyParams(reply_msg, output);
|
||||
Send(reply_msg);
|
||||
if (output.status !=
|
||||
CefViewHostMsg_GetPluginInfo_Status::kNotFound) {
|
||||
main_thread_task_runner_->PostTask(
|
||||
FROM_HERE, base::Bind(&ReportMetrics, output.actual_mime_type,
|
||||
params.url, params.top_origin_url));
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(ENABLE_PEPPER_CDMS)
|
||||
|
||||
void CefPluginInfoMessageFilter::OnIsInternalPluginAvailableForMimeType(
|
||||
const std::string& mime_type,
|
||||
bool* is_available,
|
||||
std::vector<base::string16>* additional_param_names,
|
||||
std::vector<base::string16>* additional_param_values) {
|
||||
std::vector<WebPluginInfo> plugins;
|
||||
PluginService::GetInstance()->GetInternalPlugins(&plugins);
|
||||
|
||||
bool is_plugin_disabled = false;
|
||||
for (size_t i = 0; i < plugins.size(); ++i) {
|
||||
const WebPluginInfo& plugin = plugins[i];
|
||||
const std::vector<content::WebPluginMimeType>& mime_types =
|
||||
plugin.mime_types;
|
||||
for (size_t j = 0; j < mime_types.size(); ++j) {
|
||||
if (mime_types[j].mime_type == mime_type) {
|
||||
if (!context_.IsPluginEnabled(plugin)) {
|
||||
is_plugin_disabled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
*is_available = true;
|
||||
*additional_param_names = mime_types[j].additional_param_names;
|
||||
*additional_param_values = mime_types[j].additional_param_values;
|
||||
SendPluginAvailabilityUMA(mime_type, PLUGIN_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*is_available = false;
|
||||
SendPluginAvailabilityUMA(
|
||||
mime_type, is_plugin_disabled ? PLUGIN_DISABLED : PLUGIN_NOT_REGISTERED);
|
||||
}
|
||||
|
||||
#endif // defined(ENABLE_PEPPER_CDMS)
|
||||
|
||||
void CefPluginInfoMessageFilter::Context::DecidePluginStatus(
|
||||
const GetPluginInfo_Params& params,
|
||||
const WebPluginInfo& plugin,
|
||||
const PluginMetadata* plugin_metadata,
|
||||
CefViewHostMsg_GetPluginInfo_Status* status) const {
|
||||
#if defined(OS_WIN)
|
||||
if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI &&
|
||||
base::win::IsMetroProcess()) {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (plugin.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) {
|
||||
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
|
||||
// NPAPI plugins are not supported inside <webview> guests.
|
||||
#if defined(ENABLE_EXTENSIONS)
|
||||
if (extensions::WebViewRendererState::GetInstance()->IsGuest(
|
||||
render_process_id_)) {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ContentSetting plugin_setting = CONTENT_SETTING_DEFAULT;
|
||||
bool uses_default_content_setting = true;
|
||||
bool is_managed = false;
|
||||
// Check plugin content settings. The primary URL is the top origin URL and
|
||||
// the secondary URL is the plugin URL.
|
||||
GetPluginContentSetting(plugin, params.top_origin_url, params.url,
|
||||
plugin_metadata->identifier(), &plugin_setting,
|
||||
&uses_default_content_setting, &is_managed);
|
||||
|
||||
// TODO(tommycli): Remove once we deprecate the plugin ASK policy.
|
||||
bool legacy_ask_user = plugin_setting == CONTENT_SETTING_ASK;
|
||||
plugin_setting = content_settings::PluginsFieldTrial::EffectiveContentSetting(
|
||||
CONTENT_SETTINGS_TYPE_PLUGINS, plugin_setting);
|
||||
|
||||
DCHECK(plugin_setting != CONTENT_SETTING_DEFAULT);
|
||||
DCHECK(plugin_setting != CONTENT_SETTING_ASK);
|
||||
|
||||
PluginMetadata::SecurityStatus plugin_status =
|
||||
plugin_metadata->GetSecurityStatus(plugin);
|
||||
#if defined(ENABLE_PLUGIN_INSTALLATION)
|
||||
// Check if the plugin is outdated.
|
||||
if (plugin_status == PluginMetadata::SECURITY_STATUS_OUT_OF_DATE &&
|
||||
!allow_outdated_plugins_.GetValue()) {
|
||||
if (allow_outdated_plugins_.IsManaged()) {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed;
|
||||
} else {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// Check if the plugin requires authorization.
|
||||
if (plugin_status ==
|
||||
PluginMetadata::SECURITY_STATUS_REQUIRES_AUTHORIZATION &&
|
||||
plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS &&
|
||||
plugin.type != WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS &&
|
||||
plugin.type != WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN &&
|
||||
!always_authorize_plugins_.GetValue() &&
|
||||
plugin_setting != CONTENT_SETTING_BLOCK &&
|
||||
uses_default_content_setting) {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kUnauthorized;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the plugin is crashing too much.
|
||||
if (PluginService::GetInstance()->IsPluginUnstable(plugin.path) &&
|
||||
!always_authorize_plugins_.GetValue() &&
|
||||
plugin_setting != CONTENT_SETTING_BLOCK &&
|
||||
uses_default_content_setting) {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kUnauthorized;
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_EXTENSIONS)
|
||||
// If an app has explicitly made internal resources available by listing them
|
||||
// in |accessible_resources| in the manifest, then allow them to be loaded by
|
||||
// plugins inside a guest-view.
|
||||
if (params.url.SchemeIs(extensions::kExtensionScheme) && !is_managed &&
|
||||
plugin_setting == CONTENT_SETTING_BLOCK &&
|
||||
IsPluginLoadingAccessibleResourceInWebView(
|
||||
extension_registry_, render_process_id_, params.url)) {
|
||||
plugin_setting = CONTENT_SETTING_ALLOW;
|
||||
}
|
||||
#endif // defined(ENABLE_EXTENSIONS)
|
||||
|
||||
if (plugin_setting == CONTENT_SETTING_DETECT_IMPORTANT_CONTENT) {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kPlayImportantContent;
|
||||
} else if (plugin_setting == CONTENT_SETTING_BLOCK) {
|
||||
// For managed users with the ASK policy, we allow manually running plugins
|
||||
// via context menu. This is the closest to admin intent.
|
||||
*status = is_managed && !legacy_ask_user
|
||||
? CefViewHostMsg_GetPluginInfo_Status::kBlockedByPolicy
|
||||
: CefViewHostMsg_GetPluginInfo_Status::kBlocked;
|
||||
}
|
||||
|
||||
if (*status == CefViewHostMsg_GetPluginInfo_Status::kAllowed) {
|
||||
// Allow an embedder of <webview> to block a plugin from being loaded inside
|
||||
// the guest. In order to do this, set the status to 'Unauthorized' here,
|
||||
// and update the status as appropriate depending on the response from the
|
||||
// embedder.
|
||||
#if defined(ENABLE_EXTENSIONS)
|
||||
if (extensions::WebViewRendererState::GetInstance()->IsGuest(
|
||||
render_process_id_)) {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kUnauthorized;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool CefPluginInfoMessageFilter::Context::FindEnabledPlugin(
|
||||
int render_frame_id,
|
||||
const GURL& url,
|
||||
const GURL& top_origin_url,
|
||||
const std::string& mime_type,
|
||||
CefViewHostMsg_GetPluginInfo_Status* status,
|
||||
WebPluginInfo* plugin,
|
||||
std::string* actual_mime_type,
|
||||
scoped_ptr<PluginMetadata>* plugin_metadata) const {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kAllowed;
|
||||
|
||||
bool allow_wildcard = true;
|
||||
std::vector<WebPluginInfo> matching_plugins;
|
||||
std::vector<std::string> mime_types;
|
||||
PluginService::GetInstance()->GetPluginInfoArray(
|
||||
url, mime_type, allow_wildcard, &matching_plugins, &mime_types);
|
||||
if (matching_plugins.empty()) {
|
||||
*status = CefViewHostMsg_GetPluginInfo_Status::kNotFound;
|
||||
#if defined(OS_WIN) || defined(OS_MACOSX)
|
||||
if (!PluginService::GetInstance()->NPAPIPluginsSupported()) {
|
||||
// At this point it is not known for sure this is an NPAPI plugin as it
|
||||
// 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)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
content::PluginServiceFilter* filter =
|
||||
PluginService::GetInstance()->GetFilter();
|
||||
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])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
*plugin_metadata = PluginFinder::GetInstance()->GetPluginMetadata(*plugin);
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void CefPluginInfoMessageFilter::Context::GetPluginContentSetting(
|
||||
const WebPluginInfo& plugin,
|
||||
const GURL& policy_url,
|
||||
const GURL& plugin_url,
|
||||
const std::string& resource,
|
||||
ContentSetting* setting,
|
||||
bool* uses_default_content_setting,
|
||||
bool* is_managed) const {
|
||||
scoped_ptr<base::Value> value;
|
||||
content_settings::SettingInfo info;
|
||||
bool uses_plugin_specific_setting = false;
|
||||
if (ShouldUseJavaScriptSettingForPlugin(plugin)) {
|
||||
value = host_content_settings_map_->GetWebsiteSetting(
|
||||
policy_url,
|
||||
policy_url,
|
||||
CONTENT_SETTINGS_TYPE_JAVASCRIPT,
|
||||
std::string(),
|
||||
&info);
|
||||
} else {
|
||||
content_settings::SettingInfo specific_info;
|
||||
scoped_ptr<base::Value> specific_setting =
|
||||
host_content_settings_map_->GetWebsiteSetting(
|
||||
policy_url,
|
||||
plugin_url,
|
||||
CONTENT_SETTINGS_TYPE_PLUGINS,
|
||||
resource,
|
||||
&specific_info);
|
||||
content_settings::SettingInfo general_info;
|
||||
scoped_ptr<base::Value> general_setting =
|
||||
host_content_settings_map_->GetWebsiteSetting(
|
||||
policy_url,
|
||||
plugin_url,
|
||||
CONTENT_SETTINGS_TYPE_PLUGINS,
|
||||
std::string(),
|
||||
&general_info);
|
||||
|
||||
// If there is a plugin-specific setting, we use it, unless the general
|
||||
// setting was set by policy, in which case it takes precedence.
|
||||
// TODO(tommycli): Remove once we deprecate the plugin ASK policy.
|
||||
bool legacy_ask_user = content_settings::ValueToContentSetting(
|
||||
general_setting.get()) == CONTENT_SETTING_ASK;
|
||||
bool use_policy =
|
||||
general_info.source == content_settings::SETTING_SOURCE_POLICY &&
|
||||
!legacy_ask_user;
|
||||
uses_plugin_specific_setting = specific_setting && !use_policy;
|
||||
if (uses_plugin_specific_setting) {
|
||||
value = specific_setting.Pass();
|
||||
info = specific_info;
|
||||
} else {
|
||||
value = general_setting.Pass();
|
||||
info = general_info;
|
||||
}
|
||||
}
|
||||
*setting = content_settings::ValueToContentSetting(value.get());
|
||||
*uses_default_content_setting =
|
||||
!uses_plugin_specific_setting &&
|
||||
info.primary_pattern == ContentSettingsPattern::Wildcard() &&
|
||||
info.secondary_pattern == ContentSettingsPattern::Wildcard();
|
||||
*is_managed = info.source == content_settings::SETTING_SOURCE_POLICY;
|
||||
}
|
||||
|
||||
bool CefPluginInfoMessageFilter::Context::IsPluginEnabled(
|
||||
const content::WebPluginInfo& plugin) const {
|
||||
return true;
|
||||
}
|
@@ -3,8 +3,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CEF_LIBCEF_BROWSER_EXTENSIONS_PLUGIN_INFO_MESSAGE_FILTER_H_
|
||||
#define CEF_LIBCEF_BROWSER_EXTENSIONS_PLUGIN_INFO_MESSAGE_FILTER_H_
|
||||
#ifndef CEF_LIBCEF_BROWSER_PLUGINS_PLUGIN_INFO_MESSAGE_FILTER_H_
|
||||
#define CEF_LIBCEF_BROWSER_PLUGINS_PLUGIN_INFO_MESSAGE_FILTER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -14,19 +14,24 @@
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/prefs/pref_member.h"
|
||||
#include "base/sequenced_task_runner_helpers.h"
|
||||
#include "chrome/browser/plugins/plugin_metadata.h"
|
||||
#include "components/content_settings/core/browser/host_content_settings_map.h"
|
||||
#include "components/content_settings/core/common/content_settings.h"
|
||||
#include "content/public/browser/browser_message_filter.h"
|
||||
|
||||
class CefBrowserContext;
|
||||
struct CefViewHostMsg_GetPluginInfo_Output;
|
||||
enum class CefViewHostMsg_GetPluginInfo_Status;
|
||||
class GURL;
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
class ResourceContext;
|
||||
struct WebPluginInfo;
|
||||
}
|
||||
|
||||
namespace extensions {
|
||||
class ExtensionRegistry;
|
||||
}
|
||||
|
||||
// This class filters out incoming IPC messages requesting plugin information.
|
||||
class CefPluginInfoMessageFilter : public content::BrowserMessageFilter {
|
||||
@@ -36,13 +41,14 @@ class CefPluginInfoMessageFilter : public content::BrowserMessageFilter {
|
||||
// Contains all the information needed by the CefPluginInfoMessageFilter.
|
||||
class Context {
|
||||
public:
|
||||
Context(int render_process_id, content::BrowserContext* browser_context);
|
||||
Context(int render_process_id, CefBrowserContext* profile);
|
||||
|
||||
~Context();
|
||||
|
||||
void DecidePluginStatus(
|
||||
const GetPluginInfo_Params& params,
|
||||
const content::WebPluginInfo& plugin,
|
||||
const PluginMetadata* plugin_metadata,
|
||||
CefViewHostMsg_GetPluginInfo_Status* status) const;
|
||||
bool FindEnabledPlugin(int render_frame_id,
|
||||
const GURL& url,
|
||||
@@ -50,15 +56,33 @@ class CefPluginInfoMessageFilter : public content::BrowserMessageFilter {
|
||||
const std::string& mime_type,
|
||||
CefViewHostMsg_GetPluginInfo_Status* status,
|
||||
content::WebPluginInfo* plugin,
|
||||
std::string* actual_mime_type) const;
|
||||
std::string* actual_mime_type,
|
||||
scoped_ptr<PluginMetadata>* plugin_metadata) const;
|
||||
void GetPluginContentSetting(const content::WebPluginInfo& plugin,
|
||||
const GURL& policy_url,
|
||||
const GURL& plugin_url,
|
||||
const std::string& resource,
|
||||
ContentSetting* setting,
|
||||
bool* is_default,
|
||||
bool* is_managed) const;
|
||||
void MaybeGrantAccess(CefViewHostMsg_GetPluginInfo_Status status,
|
||||
const base::FilePath& path) const;
|
||||
bool IsPluginEnabled(const content::WebPluginInfo& plugin) const;
|
||||
|
||||
private:
|
||||
int render_process_id_;
|
||||
content::ResourceContext* resource_context_;
|
||||
#if defined(ENABLE_EXTENSIONS)
|
||||
extensions::ExtensionRegistry* extension_registry_;
|
||||
#endif
|
||||
const HostContentSettingsMap* host_content_settings_map_;
|
||||
|
||||
BooleanPrefMember allow_outdated_plugins_;
|
||||
BooleanPrefMember always_authorize_plugins_;
|
||||
};
|
||||
|
||||
CefPluginInfoMessageFilter(int render_process_id,
|
||||
content::BrowserContext* browser_context);
|
||||
CefBrowserContext* profile);
|
||||
|
||||
// content::BrowserMessageFilter methods:
|
||||
bool OnMessageReceived(const IPC::Message& message) override;
|
||||
@@ -76,11 +100,6 @@ class CefPluginInfoMessageFilter : public content::BrowserMessageFilter {
|
||||
const GURL& top_origin_url,
|
||||
const std::string& mime_type,
|
||||
IPC::Message* reply_msg);
|
||||
void OnIsInternalPluginAvailableForMimeType(
|
||||
const std::string& mime_type,
|
||||
bool* is_available,
|
||||
std::vector<base::string16>* additional_param_names,
|
||||
std::vector<base::string16>* additional_param_values);
|
||||
|
||||
// |params| wraps the parameters passed to |OnGetPluginInfo|, because
|
||||
// |base::Bind| doesn't support the required arity <http://crbug.com/98542>.
|
||||
@@ -88,7 +107,23 @@ class CefPluginInfoMessageFilter : public content::BrowserMessageFilter {
|
||||
IPC::Message* reply_msg,
|
||||
const std::vector<content::WebPluginInfo>& plugins);
|
||||
|
||||
#if defined(ENABLE_PEPPER_CDMS)
|
||||
// Returns whether any internal plugin supporting |mime_type| is registered
|
||||
// and enabled. Does not determine whether the plugin can actually be
|
||||
// instantiated (e.g. whether it has all its dependencies).
|
||||
// When the returned *|is_available| is true, |additional_param_names| and
|
||||
// |additional_param_values| contain the name-value pairs, if any, specified
|
||||
// for the *first* non-disabled plugin found that is registered for
|
||||
// |mime_type|.
|
||||
void OnIsInternalPluginAvailableForMimeType(
|
||||
const std::string& mime_type,
|
||||
bool* is_available,
|
||||
std::vector<base::string16>* additional_param_names,
|
||||
std::vector<base::string16>* additional_param_values);
|
||||
#endif
|
||||
|
||||
Context context_;
|
||||
CefBrowserContext* browser_context_;
|
||||
|
||||
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
|
||||
base::WeakPtrFactory<CefPluginInfoMessageFilter> weak_ptr_factory_;
|
||||
@@ -96,6 +131,4 @@ class CefPluginInfoMessageFilter : public content::BrowserMessageFilter {
|
||||
DISALLOW_COPY_AND_ASSIGN(CefPluginInfoMessageFilter);
|
||||
};
|
||||
|
||||
} // namespace extensions
|
||||
|
||||
#endif // CEF_LIBCEF_BROWSER_EXTENSIONS_PLUGIN_INFO_MESSAGE_FILTER_H_
|
||||
#endif // CEF_LIBCEF_BROWSER_PLUGINS_PLUGIN_INFO_MESSAGE_FILTER_H_
|
@@ -163,6 +163,11 @@ IPC_MESSAGE_ROUTED1(CefMsg_Response,
|
||||
IPC_MESSAGE_ROUTED1(CefMsg_ResponseAck,
|
||||
int /* request_id */)
|
||||
|
||||
// Tells the render frame to load all blocked plugins with the given identifier.
|
||||
// Based on ChromeViewMsg_LoadBlockedPlugins.
|
||||
IPC_MESSAGE_ROUTED1(CefViewMsg_LoadBlockedPlugins,
|
||||
std::string /* identifier */)
|
||||
|
||||
// Sent on process startup to indicate whether this process is running in
|
||||
// incognito mode. Based on ChromeViewMsg_SetIsIncognitoProcess.
|
||||
IPC_MESSAGE_CONTROL1(CefProcessMsg_SetIsIncognitoProcess,
|
||||
|
@@ -116,4 +116,14 @@ const char kWidevineCdmPath[] = "widevine-cdm-path";
|
||||
// Widevine CDM version.
|
||||
const char kWidevineCdmVersion[] = "widevine-cdm-version";
|
||||
|
||||
// Default plugin policy action.
|
||||
const char kPluginPolicy[] = "plugin-policy";
|
||||
// Allow the content. This is the default value.
|
||||
const char kPluginPolicy_Allow[] = "allow";
|
||||
// Allow important content and block unimportant content based on heuristics.
|
||||
// The user can manually load blocked content.
|
||||
const char kPluginPolicy_Detect[] = "detect";
|
||||
// Block the content. The user can manually load blocked content.
|
||||
const char kPluginPolicy_Block[] = "block";
|
||||
|
||||
} // namespace switches
|
||||
|
@@ -48,6 +48,10 @@ extern const char kEnableNPAPI[];
|
||||
extern const char kEnableWidevineCdm[];
|
||||
extern const char kWidevineCdmPath[];
|
||||
extern const char kWidevineCdmVersion[];
|
||||
extern const char kPluginPolicy[];
|
||||
extern const char kPluginPolicy_Allow[];
|
||||
extern const char kPluginPolicy_Detect[];
|
||||
extern const char kPluginPolicy_Block[];
|
||||
|
||||
} // namespace switches
|
||||
|
||||
|
@@ -433,8 +433,15 @@ bool CefContentClient::GetRawDataResource(int resource_id,
|
||||
if (handler.get()) {
|
||||
void* data = NULL;
|
||||
size_t data_size = 0;
|
||||
if (handler->GetDataResource(resource_id, data, data_size))
|
||||
if (scale_factor != ui::SCALE_FACTOR_NONE) {
|
||||
if (handler->GetDataResourceForScale(
|
||||
resource_id, static_cast<cef_scale_factor_t>(scale_factor), data,
|
||||
data_size)) {
|
||||
*value = base::StringPiece(static_cast<char*>(data), data_size);
|
||||
}
|
||||
} else if (handler->GetDataResource(resource_id, data, data_size)) {
|
||||
*value = base::StringPiece(static_cast<char*>(data), data_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -27,10 +27,12 @@
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#include "chrome/common/widevine_cdm_constants.h"
|
||||
#include "components/content_settings/core/common/content_settings_pattern.h"
|
||||
#include "content/public/browser/browser_main_runner.h"
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "content/public/common/main_function_params.h"
|
||||
#include "extensions/common/constants.h"
|
||||
#include "pdf/pdf.h"
|
||||
#include "ui/base/layout.h"
|
||||
#include "ui/base/resource/resource_bundle.h"
|
||||
@@ -483,6 +485,9 @@ bool CefMainDelegate::BasicStartupComplete(int* exit_code) {
|
||||
|
||||
logging::InitLogging(log_settings);
|
||||
|
||||
ContentSettingsPattern::SetNonWildcardDomainNonPortScheme(
|
||||
extensions::kExtensionScheme);
|
||||
|
||||
content::SetContentClient(&content_client_);
|
||||
|
||||
return false;
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#include "libcef/renderer/webkit_glue.h"
|
||||
|
||||
#include "base/base64.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
#include "net/base/escape.h"
|
||||
#include "net/base/mime_util.h"
|
||||
#include "third_party/WebKit/public/platform/WebString.h"
|
||||
@@ -76,6 +77,10 @@ bool CefCreateURL(const CefURLParts& parts,
|
||||
}
|
||||
|
||||
CefString CefGetMimeType(const CefString& extension) {
|
||||
// Requests should not block on the disk! On POSIX this goes to disk.
|
||||
// http://code.google.com/p/chromium/issues/detail?id=59849
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
|
||||
std::string mime_type;
|
||||
net::GetMimeTypeFromExtension(extension, &mime_type);
|
||||
return mime_type;
|
||||
|
47
libcef/common/resource_bundle_impl.cc
Normal file
47
libcef/common/resource_bundle_impl.cc
Normal file
@@ -0,0 +1,47 @@
|
||||
// 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/common/resource_bundle_impl.h"
|
||||
|
||||
#include "ui/base/resource/resource_bundle.h"
|
||||
|
||||
CefResourceBundleImpl::CefResourceBundleImpl() {
|
||||
}
|
||||
|
||||
CefString CefResourceBundleImpl::GetLocalizedString(int string_id) {
|
||||
if (!ui::ResourceBundle::HasSharedInstance())
|
||||
return CefString();
|
||||
|
||||
return ui::ResourceBundle::GetSharedInstance().GetLocalizedString(string_id);
|
||||
}
|
||||
|
||||
bool CefResourceBundleImpl::GetDataResource(int resource_id,
|
||||
void*& data,
|
||||
size_t& data_size) {
|
||||
return GetDataResourceForScale(resource_id, SCALE_FACTOR_NONE, data,
|
||||
data_size);
|
||||
}
|
||||
|
||||
bool CefResourceBundleImpl::GetDataResourceForScale(int resource_id,
|
||||
ScaleFactor scale_factor,
|
||||
void*& data,
|
||||
size_t& data_size) {
|
||||
if (!ui::ResourceBundle::HasSharedInstance())
|
||||
return false;
|
||||
|
||||
const base::StringPiece& result =
|
||||
ui::ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
|
||||
resource_id, static_cast<ui::ScaleFactor>(scale_factor));
|
||||
if (result.empty())
|
||||
return false;
|
||||
|
||||
data = const_cast<char*>(result.data());
|
||||
data_size = result.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
CefRefPtr<CefResourceBundle> CefResourceBundle::GetGlobal() {
|
||||
return new CefResourceBundleImpl();
|
||||
}
|
31
libcef/common/resource_bundle_impl.h
Normal file
31
libcef/common/resource_bundle_impl.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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_COMMON_RESOURCE_BUNDLE_IMPL_H_
|
||||
#define CEF_LIBCEF_COMMON_RESOURCE_BUNDLE_IMPL_H_
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "include/cef_resource_bundle.h"
|
||||
|
||||
class CefResourceBundleImpl : public CefResourceBundle {
|
||||
public:
|
||||
CefResourceBundleImpl();
|
||||
|
||||
// CefResourceBundle methods.
|
||||
CefString GetLocalizedString(int string_id) override;
|
||||
bool GetDataResource(int resource_id,
|
||||
void*& data,
|
||||
size_t& data_size) override;
|
||||
bool GetDataResourceForScale(int resource_id,
|
||||
ScaleFactor scale_factor,
|
||||
void*& data,
|
||||
size_t& data_size) override;
|
||||
|
||||
private:
|
||||
IMPLEMENT_REFCOUNTING(CefResourceBundleImpl);
|
||||
DISALLOW_COPY_AND_ASSIGN(CefResourceBundleImpl);
|
||||
};
|
||||
|
||||
#endif // CEF_LIBCEF_COMMON_RESOURCE_BUNDLE_IMPL_H_
|
@@ -19,6 +19,8 @@
|
||||
#include "libcef/renderer/extensions/print_web_view_helper_delegate.h"
|
||||
#include "libcef/renderer/media/cef_key_systems.h"
|
||||
#include "libcef/renderer/pepper/pepper_helper.h"
|
||||
#include "libcef/renderer/plugins/cef_plugin_placeholder.h"
|
||||
#include "libcef/renderer/plugins/plugin_preroller.h"
|
||||
#include "libcef/renderer/render_frame_observer.h"
|
||||
#include "libcef/renderer/render_message_filter.h"
|
||||
#include "libcef/renderer/render_process_observer.h"
|
||||
@@ -27,15 +29,20 @@
|
||||
#include "libcef/renderer/webkit_glue.h"
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/metrics/user_metrics_action.h"
|
||||
#include "base/path_service.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#include "chrome/common/pepper_permission_util.h"
|
||||
#include "chrome/grit/generated_resources.h"
|
||||
#include "chrome/renderer/content_settings_observer.h"
|
||||
#include "chrome/renderer/loadtimes_extension_bindings.h"
|
||||
#include "chrome/renderer/pepper/chrome_pdf_print_client.h"
|
||||
#include "chrome/renderer/spellchecker/spellcheck.h"
|
||||
#include "chrome/renderer/spellchecker/spellcheck_provider.h"
|
||||
#include "components/content_settings/core/common/content_settings_types.h"
|
||||
#include "components/nacl/common/nacl_constants.h"
|
||||
#include "components/printing/renderer/print_web_view_helper.h"
|
||||
#include "components/web_cache/renderer/web_cache_render_process_observer.h"
|
||||
#include "content/child/worker_task_runner.h"
|
||||
@@ -62,6 +69,7 @@
|
||||
#include "third_party/WebKit/public/platform/WebPrerenderingSupport.h"
|
||||
#include "third_party/WebKit/public/platform/WebString.h"
|
||||
#include "third_party/WebKit/public/platform/WebURL.h"
|
||||
#include "third_party/WebKit/public/web/WebConsoleMessage.h"
|
||||
#include "third_party/WebKit/public/web/WebDocument.h"
|
||||
#include "third_party/WebKit/public/web/WebElement.h"
|
||||
#include "third_party/WebKit/public/web/WebFrame.h"
|
||||
@@ -71,6 +79,7 @@
|
||||
#include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
|
||||
#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
|
||||
#include "third_party/WebKit/public/web/WebView.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#include "base/mac/mac_util.h"
|
||||
@@ -186,6 +195,19 @@ void AppendParams(const std::vector<base::string16>& additional_names,
|
||||
existing_values->swap(values);
|
||||
}
|
||||
|
||||
std::string GetPluginInstancePosterAttribute(
|
||||
const blink::WebPluginParams& params) {
|
||||
DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size());
|
||||
|
||||
for (size_t i = 0; i < params.attributeNames.size(); ++i) {
|
||||
if (params.attributeNames[i].utf8() == "poster" &&
|
||||
!params.attributeValues[i].isEmpty()) {
|
||||
return params.attributeValues[i].utf8();
|
||||
}
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CefContentRendererClient::CefContentRendererClient()
|
||||
@@ -660,6 +682,7 @@ void CefContentRendererClient::WillDestroyCurrentMessageLoop() {
|
||||
single_process_cleanup_complete_ = true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool CefContentRendererClient::IsExtensionOrSharedModuleWhitelisted(
|
||||
const GURL& url, const std::set<std::string>& whitelist) {
|
||||
DCHECK(extensions::ExtensionsEnabled());
|
||||
@@ -669,6 +692,167 @@ bool CefContentRendererClient::IsExtensionOrSharedModuleWhitelisted(
|
||||
whitelist);
|
||||
}
|
||||
|
||||
// static
|
||||
blink::WebPlugin* CefContentRendererClient::CreatePlugin(
|
||||
content::RenderFrame* render_frame,
|
||||
blink::WebLocalFrame* frame,
|
||||
const blink::WebPluginParams& original_params,
|
||||
const CefViewHostMsg_GetPluginInfo_Output& output) {
|
||||
const content::WebPluginInfo& info = output.plugin;
|
||||
const std::string& actual_mime_type = output.actual_mime_type;
|
||||
const base::string16& group_name = output.group_name;
|
||||
const std::string& identifier = output.group_identifier;
|
||||
CefViewHostMsg_GetPluginInfo_Status status = output.status;
|
||||
GURL url(original_params.url);
|
||||
std::string orig_mime_type = original_params.mimeType.utf8();
|
||||
CefPluginPlaceholder* placeholder = NULL;
|
||||
|
||||
// If the browser plugin is to be enabled, this should be handled by the
|
||||
// renderer, so the code won't reach here due to the early exit in
|
||||
// OverrideCreatePlugin.
|
||||
if (status == CefViewHostMsg_GetPluginInfo_Status::kNotFound ||
|
||||
orig_mime_type == content::kBrowserPluginMimeType) {
|
||||
placeholder = CefPluginPlaceholder::CreateLoadableMissingPlugin(
|
||||
render_frame, frame, original_params);
|
||||
} else {
|
||||
// TODO(bauerb): This should be in content/.
|
||||
blink::WebPluginParams params(original_params);
|
||||
for (size_t i = 0; i < info.mime_types.size(); ++i) {
|
||||
if (info.mime_types[i].mime_type == actual_mime_type) {
|
||||
AppendParams(info.mime_types[i].additional_param_names,
|
||||
info.mime_types[i].additional_param_values,
|
||||
¶ms.attributeNames, ¶ms.attributeValues);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (params.mimeType.isNull() && (actual_mime_type.size() > 0)) {
|
||||
// Webkit might say that mime type is null while we already know the
|
||||
// actual mime type via CefViewHostMsg_GetPluginInfo. In that case
|
||||
// we should use what we know since WebpluginDelegateProxy does some
|
||||
// specific initializations based on this information.
|
||||
params.mimeType = blink::WebString::fromUTF8(actual_mime_type.c_str());
|
||||
}
|
||||
|
||||
auto create_blocked_plugin =
|
||||
[&render_frame, &frame, ¶ms, &info, &identifier, &group_name](
|
||||
int template_id, const base::string16& message) {
|
||||
return CefPluginPlaceholder::CreateBlockedPlugin(
|
||||
render_frame, frame, params, info, identifier, group_name,
|
||||
template_id, message, PlaceholderPosterInfo());
|
||||
};
|
||||
switch (status) {
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kNotFound: {
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kAllowed:
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kPlayImportantContent: {
|
||||
// Delay loading plugins if prerendering.
|
||||
// TODO(mmenke): In the case of prerendering, feed into
|
||||
// CefContentRendererClient::CreatePlugin instead, to
|
||||
// reduce the chance of future regressions.
|
||||
bool is_prerendering = false;
|
||||
bool power_saver_enabled =
|
||||
status ==
|
||||
CefViewHostMsg_GetPluginInfo_Status::kPlayImportantContent;
|
||||
bool blocked_for_background_tab =
|
||||
render_frame->IsHidden() && power_saver_enabled;
|
||||
|
||||
PlaceholderPosterInfo poster_info;
|
||||
if (power_saver_enabled) {
|
||||
poster_info.poster_attribute =
|
||||
GetPluginInstancePosterAttribute(params);
|
||||
poster_info.base_url = frame->document().url();
|
||||
}
|
||||
|
||||
if (blocked_for_background_tab || is_prerendering ||
|
||||
!poster_info.poster_attribute.empty()) {
|
||||
placeholder = CefPluginPlaceholder::CreateBlockedPlugin(
|
||||
render_frame, frame, params, info, identifier, group_name,
|
||||
poster_info.poster_attribute.empty() ? IDR_BLOCKED_PLUGIN_HTML
|
||||
: IDR_PLUGIN_POSTER_HTML,
|
||||
l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED, group_name),
|
||||
poster_info);
|
||||
placeholder->set_blocked_for_background_tab(
|
||||
blocked_for_background_tab);
|
||||
placeholder->set_blocked_for_prerendering(is_prerendering);
|
||||
placeholder->set_power_saver_enabled(power_saver_enabled);
|
||||
placeholder->AllowLoading();
|
||||
break;
|
||||
}
|
||||
|
||||
scoped_ptr<content::PluginInstanceThrottler> throttler;
|
||||
if (power_saver_enabled) {
|
||||
throttler = content::PluginInstanceThrottler::Create();
|
||||
// PluginPreroller manages its own lifetime.
|
||||
new CefPluginPreroller(
|
||||
render_frame, frame, params, info, identifier, group_name,
|
||||
l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED, group_name),
|
||||
throttler.get());
|
||||
}
|
||||
|
||||
return render_frame->CreatePlugin(frame, info, params,
|
||||
throttler.Pass());
|
||||
}
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported: {
|
||||
content::RenderThread::Get()->RecordAction(
|
||||
base::UserMetricsAction("Plugin_NPAPINotSupported"));
|
||||
placeholder = create_blocked_plugin(
|
||||
IDR_BLOCKED_PLUGIN_HTML,
|
||||
l10n_util::GetStringUTF16(IDS_PLUGIN_NOT_SUPPORTED_METRO));
|
||||
break;
|
||||
}
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kDisabled: {
|
||||
// Intentionally using the blocked plugin resources instead of the
|
||||
// disabled plugin resources. This provides better messaging (no link to
|
||||
// chrome://plugins) and adds testing support.
|
||||
placeholder = create_blocked_plugin(
|
||||
IDR_BLOCKED_PLUGIN_HTML,
|
||||
l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED_BY_POLICY,
|
||||
group_name));
|
||||
break;
|
||||
}
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kOutdatedBlocked: {
|
||||
NOTREACHED() << "Plugin installation is not supported.";
|
||||
break;
|
||||
}
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kOutdatedDisallowed: {
|
||||
placeholder = create_blocked_plugin(
|
||||
IDR_BLOCKED_PLUGIN_HTML,
|
||||
l10n_util::GetStringFUTF16(IDS_PLUGIN_OUTDATED, group_name));
|
||||
break;
|
||||
}
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kUnauthorized: {
|
||||
placeholder = create_blocked_plugin(
|
||||
IDR_BLOCKED_PLUGIN_HTML,
|
||||
l10n_util::GetStringFUTF16(IDS_PLUGIN_NOT_AUTHORIZED, group_name));
|
||||
placeholder->AllowLoading();
|
||||
break;
|
||||
}
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kBlocked: {
|
||||
placeholder = create_blocked_plugin(
|
||||
IDR_BLOCKED_PLUGIN_HTML,
|
||||
l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED, group_name));
|
||||
placeholder->AllowLoading();
|
||||
content::RenderThread::Get()->RecordAction(
|
||||
base::UserMetricsAction("Plugin_Blocked"));
|
||||
break;
|
||||
}
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kBlockedByPolicy: {
|
||||
placeholder = create_blocked_plugin(
|
||||
IDR_BLOCKED_PLUGIN_HTML,
|
||||
l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED_BY_POLICY,
|
||||
group_name));
|
||||
content::RenderThread::Get()->RecordAction(
|
||||
base::UserMetricsAction("Plugin_BlockedByPolicy"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
placeholder->SetStatus(status);
|
||||
return placeholder->plugin();
|
||||
}
|
||||
|
||||
void CefContentRendererClient::BrowserCreated(
|
||||
content::RenderView* render_view,
|
||||
content::RenderFrame* render_frame) {
|
||||
@@ -743,57 +927,3 @@ void CefContentRendererClient::RunSingleProcessCleanupOnUIThread() {
|
||||
if (!CefContext::Get()->settings().multi_threaded_message_loop)
|
||||
delete host;
|
||||
}
|
||||
|
||||
blink::WebPlugin* CefContentRendererClient::CreatePlugin(
|
||||
content::RenderFrame* render_frame,
|
||||
blink::WebLocalFrame* frame,
|
||||
const blink::WebPluginParams& original_params,
|
||||
const CefViewHostMsg_GetPluginInfo_Output& output) {
|
||||
const content::WebPluginInfo& info = output.plugin;
|
||||
const std::string& actual_mime_type = output.actual_mime_type;
|
||||
CefViewHostMsg_GetPluginInfo_Status status = output.status;
|
||||
GURL url(original_params.url);
|
||||
std::string orig_mime_type = original_params.mimeType.utf8();
|
||||
|
||||
// If the browser plugin is to be enabled, this should be handled by the
|
||||
// renderer, so the code won't reach here due to the early exit in
|
||||
// OverrideCreatePlugin.
|
||||
if (status == CefViewHostMsg_GetPluginInfo_Status::kNotFound ||
|
||||
orig_mime_type == content::kBrowserPluginMimeType) {
|
||||
return NULL;
|
||||
} else {
|
||||
// TODO(bauerb): This should be in content/.
|
||||
blink::WebPluginParams params(original_params);
|
||||
for (size_t i = 0; i < info.mime_types.size(); ++i) {
|
||||
if (info.mime_types[i].mime_type == actual_mime_type) {
|
||||
AppendParams(info.mime_types[i].additional_param_names,
|
||||
info.mime_types[i].additional_param_values,
|
||||
¶ms.attributeNames, ¶ms.attributeValues);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (params.mimeType.isNull() && (actual_mime_type.size() > 0)) {
|
||||
// Webkit might say that mime type is null while we already know the
|
||||
// actual mime type via CefViewHostMsg_GetPluginInfo. In that case
|
||||
// we should use what we know since WebpluginDelegateProxy does some
|
||||
// specific initializations based on this information.
|
||||
params.mimeType = blink::WebString::fromUTF8(actual_mime_type.c_str());
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kNotFound: {
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kAllowed:
|
||||
case CefViewHostMsg_GetPluginInfo_Status::kPlayImportantContent: {
|
||||
// TODO(cef): Maybe supply a throttler based on power settings.
|
||||
return render_frame->CreatePlugin(frame, info, params, nullptr);
|
||||
}
|
||||
default:
|
||||
// TODO(cef): Provide a placeholder for the various failure conditions.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@@ -116,6 +116,12 @@ class CefContentRendererClient : public content::ContentRendererClient,
|
||||
static bool IsExtensionOrSharedModuleWhitelisted(
|
||||
const GURL& url, const std::set<std::string>& whitelist);
|
||||
|
||||
static blink::WebPlugin* CreatePlugin(
|
||||
content::RenderFrame* render_frame,
|
||||
blink::WebLocalFrame* frame,
|
||||
const blink::WebPluginParams& params,
|
||||
const CefViewHostMsg_GetPluginInfo_Output& output);
|
||||
|
||||
private:
|
||||
void BrowserCreated(content::RenderView* render_view,
|
||||
content::RenderFrame* render_frame);
|
||||
@@ -123,12 +129,6 @@ class CefContentRendererClient : public content::ContentRendererClient,
|
||||
// Perform cleanup work for single-process mode.
|
||||
void RunSingleProcessCleanupOnUIThread();
|
||||
|
||||
static blink::WebPlugin* CreatePlugin(
|
||||
content::RenderFrame* render_frame,
|
||||
blink::WebLocalFrame* frame,
|
||||
const blink::WebPluginParams& params,
|
||||
const CefViewHostMsg_GetPluginInfo_Output& output);
|
||||
|
||||
scoped_refptr<base::SequencedTaskRunner> render_task_runner_;
|
||||
scoped_ptr<CefRenderProcessObserver> observer_;
|
||||
scoped_ptr<web_cache::WebCacheRenderProcessObserver> web_cache_observer_;
|
||||
|
285
libcef/renderer/plugins/cef_plugin_placeholder.cc
Normal file
285
libcef/renderer/plugins/cef_plugin_placeholder.cc
Normal file
@@ -0,0 +1,285 @@
|
||||
// Copyright 2013 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 "libcef/renderer/plugins/cef_plugin_placeholder.h"
|
||||
|
||||
#include "libcef/common/cef_messages.h"
|
||||
#include "libcef/renderer/content_renderer_client.h"
|
||||
#include "libcef/renderer/plugins/plugin_preroller.h"
|
||||
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/values.h"
|
||||
#include "chrome/grit/generated_resources.h"
|
||||
#include "chrome/renderer/custom_menu_commands.h"
|
||||
#include "components/content_settings/content/common/content_settings_messages.h"
|
||||
#include "content/public/common/context_menu_params.h"
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
#include "content/public/renderer/render_thread.h"
|
||||
#include "gin/object_template_builder.h"
|
||||
#include "third_party/WebKit/public/web/WebDocument.h"
|
||||
#include "third_party/WebKit/public/web/WebInputEvent.h"
|
||||
#include "third_party/WebKit/public/web/WebLocalFrame.h"
|
||||
#include "third_party/WebKit/public/web/WebScriptSource.h"
|
||||
#include "third_party/WebKit/public/web/WebView.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/resource/resource_bundle.h"
|
||||
#include "ui/base/webui/jstemplate_builder.h"
|
||||
#include "ui/gfx/geometry/size.h"
|
||||
#include "url/url_util.h"
|
||||
|
||||
using base::UserMetricsAction;
|
||||
using content::RenderThread;
|
||||
using content::RenderView;
|
||||
|
||||
namespace {
|
||||
const CefPluginPlaceholder* g_last_active_menu = NULL;
|
||||
} // namespace
|
||||
|
||||
gin::WrapperInfo CefPluginPlaceholder::kWrapperInfo = {
|
||||
gin::kEmbedderNativeGin};
|
||||
|
||||
CefPluginPlaceholder::CefPluginPlaceholder(
|
||||
content::RenderFrame* render_frame,
|
||||
blink::WebLocalFrame* frame,
|
||||
const blink::WebPluginParams& params,
|
||||
const std::string& html_data,
|
||||
const base::string16& title)
|
||||
: plugins::LoadablePluginPlaceholder(render_frame,
|
||||
frame,
|
||||
params,
|
||||
html_data),
|
||||
status_(CefViewHostMsg_GetPluginInfo_Status::kAllowed),
|
||||
title_(title),
|
||||
has_host_(false),
|
||||
context_menu_request_id_(0) {
|
||||
RenderThread::Get()->AddObserver(this);
|
||||
}
|
||||
|
||||
CefPluginPlaceholder::~CefPluginPlaceholder() {
|
||||
RenderThread::Get()->RemoveObserver(this);
|
||||
if (context_menu_request_id_ && render_frame())
|
||||
render_frame()->CancelContextMenu(context_menu_request_id_);
|
||||
}
|
||||
|
||||
// static
|
||||
CefPluginPlaceholder* CefPluginPlaceholder::CreateLoadableMissingPlugin(
|
||||
content::RenderFrame* render_frame,
|
||||
blink::WebLocalFrame* frame,
|
||||
const blink::WebPluginParams& params) {
|
||||
const base::StringPiece template_html(
|
||||
ResourceBundle::GetSharedInstance().GetRawDataResource(
|
||||
IDR_BLOCKED_PLUGIN_HTML));
|
||||
|
||||
base::DictionaryValue values;
|
||||
values.SetString("message",
|
||||
l10n_util::GetStringUTF8(IDS_PLUGIN_NOT_SUPPORTED));
|
||||
|
||||
std::string html_data = webui::GetI18nTemplateHtml(template_html, &values);
|
||||
|
||||
// Will destroy itself when its WebViewPlugin is going away.
|
||||
return new CefPluginPlaceholder(render_frame, frame, params, html_data,
|
||||
params.mimeType);
|
||||
}
|
||||
|
||||
// static
|
||||
CefPluginPlaceholder* CefPluginPlaceholder::CreateBlockedPlugin(
|
||||
content::RenderFrame* render_frame,
|
||||
blink::WebLocalFrame* frame,
|
||||
const blink::WebPluginParams& params,
|
||||
const content::WebPluginInfo& info,
|
||||
const std::string& identifier,
|
||||
const base::string16& name,
|
||||
int template_id,
|
||||
const base::string16& message,
|
||||
const PlaceholderPosterInfo& poster_info) {
|
||||
base::DictionaryValue values;
|
||||
values.SetString("message", message);
|
||||
values.SetString("name", name);
|
||||
values.SetString("hide", l10n_util::GetStringUTF8(IDS_PLUGIN_HIDE));
|
||||
values.SetString("pluginType",
|
||||
frame->view()->mainFrame()->document().isPluginDocument()
|
||||
? "document"
|
||||
: "embedded");
|
||||
|
||||
if (!poster_info.poster_attribute.empty()) {
|
||||
values.SetString("poster", poster_info.poster_attribute);
|
||||
values.SetString("baseurl", poster_info.base_url.spec());
|
||||
|
||||
if (!poster_info.custom_poster_size.IsEmpty()) {
|
||||
float zoom_factor =
|
||||
blink::WebView::zoomLevelToZoomFactor(frame->view()->zoomLevel());
|
||||
int width = roundf(poster_info.custom_poster_size.width() / zoom_factor);
|
||||
int height =
|
||||
roundf(poster_info.custom_poster_size.height() / zoom_factor);
|
||||
values.SetString("visibleWidth", base::IntToString(width) + "px");
|
||||
values.SetString("visibleHeight", base::IntToString(height) + "px");
|
||||
}
|
||||
}
|
||||
|
||||
const base::StringPiece template_html(
|
||||
ResourceBundle::GetSharedInstance().GetRawDataResource(template_id));
|
||||
|
||||
DCHECK(!template_html.empty()) << "unable to load template. ID: "
|
||||
<< template_id;
|
||||
std::string html_data = webui::GetI18nTemplateHtml(template_html, &values);
|
||||
|
||||
// |blocked_plugin| will destroy itself when its WebViewPlugin is going away.
|
||||
CefPluginPlaceholder* blocked_plugin = new CefPluginPlaceholder(
|
||||
render_frame, frame, params, html_data, name);
|
||||
|
||||
if (!poster_info.poster_attribute.empty())
|
||||
blocked_plugin->BlockForPowerSaverPoster();
|
||||
blocked_plugin->SetPluginInfo(info);
|
||||
blocked_plugin->SetIdentifier(identifier);
|
||||
return blocked_plugin;
|
||||
}
|
||||
|
||||
void CefPluginPlaceholder::SetStatus(
|
||||
CefViewHostMsg_GetPluginInfo_Status status) {
|
||||
status_ = status;
|
||||
}
|
||||
|
||||
bool CefPluginPlaceholder::OnMessageReceived(const IPC::Message& message) {
|
||||
// We don't swallow these messages because multiple blocked plugins and other
|
||||
// objects have an interest in them.
|
||||
IPC_BEGIN_MESSAGE_MAP(CefPluginPlaceholder, message)
|
||||
IPC_MESSAGE_HANDLER(CefViewMsg_LoadBlockedPlugins, OnLoadBlockedPlugins)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CefPluginPlaceholder::OpenAboutPluginsCallback() {
|
||||
// CEF does not use IDR_DISABLED_PLUGIN_HTML which would originate this
|
||||
// callback for the chrome://plugins link.
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
void CefPluginPlaceholder::PluginListChanged() {
|
||||
if (!GetFrame() || !plugin())
|
||||
return;
|
||||
blink::WebDocument document = GetFrame()->top()->document();
|
||||
if (document.isNull())
|
||||
return;
|
||||
|
||||
CefViewHostMsg_GetPluginInfo_Output output;
|
||||
std::string mime_type(GetPluginParams().mimeType.utf8());
|
||||
blink::WebString top_origin = GetFrame()->top()->securityOrigin().toString();
|
||||
render_frame()->Send(
|
||||
new CefViewHostMsg_GetPluginInfo(routing_id(),
|
||||
GURL(GetPluginParams().url),
|
||||
GURL(top_origin),
|
||||
mime_type,
|
||||
&output));
|
||||
if (output.status == status_)
|
||||
return;
|
||||
blink::WebPlugin* new_plugin = CefContentRendererClient::CreatePlugin(
|
||||
render_frame(), GetFrame(), GetPluginParams(), output);
|
||||
ReplacePlugin(new_plugin);
|
||||
}
|
||||
|
||||
void CefPluginPlaceholder::OnMenuAction(int request_id, unsigned action) {
|
||||
DCHECK_EQ(context_menu_request_id_, request_id);
|
||||
if (g_last_active_menu != this)
|
||||
return;
|
||||
switch (action) {
|
||||
case chrome::MENU_COMMAND_PLUGIN_RUN: {
|
||||
RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Load_Menu"));
|
||||
MarkPluginEssential(
|
||||
content::PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_CLICK);
|
||||
LoadPlugin();
|
||||
break;
|
||||
}
|
||||
case chrome::MENU_COMMAND_PLUGIN_HIDE: {
|
||||
RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Hide_Menu"));
|
||||
HidePlugin();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NOTREACHED();
|
||||
}
|
||||
}
|
||||
|
||||
void CefPluginPlaceholder::OnMenuClosed(int request_id) {
|
||||
DCHECK_EQ(context_menu_request_id_, request_id);
|
||||
context_menu_request_id_ = 0;
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> CefPluginPlaceholder::GetV8Handle(
|
||||
v8::Isolate* isolate) {
|
||||
return gin::CreateHandle(isolate, this).ToV8();
|
||||
}
|
||||
|
||||
void CefPluginPlaceholder::ShowContextMenu(
|
||||
const blink::WebMouseEvent& event) {
|
||||
if (context_menu_request_id_)
|
||||
return; // Don't allow nested context menu requests.
|
||||
|
||||
content::ContextMenuParams params;
|
||||
|
||||
if (!title_.empty()) {
|
||||
content::MenuItem name_item;
|
||||
name_item.label = title_;
|
||||
params.custom_items.push_back(name_item);
|
||||
|
||||
content::MenuItem separator_item;
|
||||
separator_item.type = content::MenuItem::SEPARATOR;
|
||||
params.custom_items.push_back(separator_item);
|
||||
}
|
||||
|
||||
if (!GetPluginInfo().path.value().empty()) {
|
||||
content::MenuItem run_item;
|
||||
run_item.action = chrome::MENU_COMMAND_PLUGIN_RUN;
|
||||
// Disable this menu item if the plugin is blocked by policy.
|
||||
run_item.enabled = LoadingAllowed();
|
||||
run_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_RUN);
|
||||
params.custom_items.push_back(run_item);
|
||||
}
|
||||
|
||||
content::MenuItem hide_item;
|
||||
hide_item.action = chrome::MENU_COMMAND_PLUGIN_HIDE;
|
||||
hide_item.enabled =
|
||||
!GetFrame()->view()->mainFrame()->document().isPluginDocument();
|
||||
hide_item.label = l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLUGIN_HIDE);
|
||||
params.custom_items.push_back(hide_item);
|
||||
|
||||
params.x = event.windowX;
|
||||
params.y = event.windowY;
|
||||
|
||||
context_menu_request_id_ = render_frame()->ShowContextMenu(this, params);
|
||||
g_last_active_menu = this;
|
||||
}
|
||||
|
||||
blink::WebPlugin* CefPluginPlaceholder::CreatePlugin() {
|
||||
scoped_ptr<content::PluginInstanceThrottler> throttler;
|
||||
// If the plugin has already been marked essential in its placeholder form,
|
||||
// we shouldn't create a new throttler and start the process all over again.
|
||||
if (power_saver_enabled()) {
|
||||
throttler = content::PluginInstanceThrottler::Create();
|
||||
// PluginPreroller manages its own lifetime.
|
||||
new CefPluginPreroller(
|
||||
render_frame(), GetFrame(), GetPluginParams(),
|
||||
GetPluginInfo(), GetIdentifier(), title_,
|
||||
l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED, title_),
|
||||
throttler.get());
|
||||
}
|
||||
return render_frame()->CreatePlugin(GetFrame(), GetPluginInfo(),
|
||||
GetPluginParams(), throttler.Pass());
|
||||
}
|
||||
|
||||
gin::ObjectTemplateBuilder CefPluginPlaceholder::GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) {
|
||||
return gin::Wrappable<CefPluginPlaceholder>::GetObjectTemplateBuilder(
|
||||
isolate)
|
||||
.SetMethod<void (CefPluginPlaceholder::*)()>(
|
||||
"hide", &CefPluginPlaceholder::HideCallback)
|
||||
.SetMethod<void (CefPluginPlaceholder::*)()>(
|
||||
"load", &CefPluginPlaceholder::LoadCallback)
|
||||
.SetMethod<void (CefPluginPlaceholder::*)()>(
|
||||
"didFinishLoading",
|
||||
&CefPluginPlaceholder::DidFinishLoadingCallback)
|
||||
.SetMethod("openAboutPlugins",
|
||||
&CefPluginPlaceholder::OpenAboutPluginsCallback);
|
||||
}
|
102
libcef/renderer/plugins/cef_plugin_placeholder.h
Normal file
102
libcef/renderer/plugins/cef_plugin_placeholder.h
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
#ifndef CEF_LIBCEF_RENDERER_PLUGINS_CEF_PLUGIN_PLACEHOLDER_H_
|
||||
#define CEF_LIBCEF_RENDERER_PLUGINS_CEF_PLUGIN_PLACEHOLDER_H_
|
||||
|
||||
#include "components/plugins/renderer/loadable_plugin_placeholder.h"
|
||||
#include "content/public/renderer/context_menu_client.h"
|
||||
#include "content/public/renderer/render_process_observer.h"
|
||||
|
||||
namespace gfx {
|
||||
class Size;
|
||||
}
|
||||
|
||||
enum class CefViewHostMsg_GetPluginInfo_Status;
|
||||
|
||||
// This contains information specifying the poster image of plugin placeholders.
|
||||
// The default constructor specifies no poster image.
|
||||
struct PlaceholderPosterInfo {
|
||||
// The poster image specified in image 'srcset' attribute format.
|
||||
std::string poster_attribute;
|
||||
|
||||
// Used to resolve relative paths in |poster_attribute|.
|
||||
GURL base_url;
|
||||
|
||||
// Specify this to provide partially obscured plugins a centered poster image.
|
||||
gfx::Size custom_poster_size;
|
||||
};
|
||||
|
||||
class CefPluginPlaceholder final
|
||||
: public plugins::LoadablePluginPlaceholder,
|
||||
public content::RenderProcessObserver,
|
||||
public content::ContextMenuClient,
|
||||
public gin::Wrappable<CefPluginPlaceholder> {
|
||||
public:
|
||||
static gin::WrapperInfo kWrapperInfo;
|
||||
|
||||
static CefPluginPlaceholder* CreateBlockedPlugin(
|
||||
content::RenderFrame* render_frame,
|
||||
blink::WebLocalFrame* frame,
|
||||
const blink::WebPluginParams& params,
|
||||
const content::WebPluginInfo& info,
|
||||
const std::string& identifier,
|
||||
const base::string16& name,
|
||||
int resource_id,
|
||||
const base::string16& message,
|
||||
const PlaceholderPosterInfo& poster_info);
|
||||
|
||||
// Creates a new WebViewPlugin with a MissingPlugin as a delegate.
|
||||
static CefPluginPlaceholder* CreateLoadableMissingPlugin(
|
||||
content::RenderFrame* render_frame,
|
||||
blink::WebLocalFrame* frame,
|
||||
const blink::WebPluginParams& params);
|
||||
|
||||
void SetStatus(CefViewHostMsg_GetPluginInfo_Status status);
|
||||
|
||||
private:
|
||||
CefPluginPlaceholder(content::RenderFrame* render_frame,
|
||||
blink::WebLocalFrame* frame,
|
||||
const blink::WebPluginParams& params,
|
||||
const std::string& html_data,
|
||||
const base::string16& title);
|
||||
~CefPluginPlaceholder() override;
|
||||
|
||||
// content::LoadablePluginPlaceholder method
|
||||
blink::WebPlugin* CreatePlugin() override;
|
||||
|
||||
// gin::Wrappable (via PluginPlaceholder) method
|
||||
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
|
||||
v8::Isolate* isolate) final;
|
||||
|
||||
// content::RenderViewObserver (via PluginPlaceholder) override:
|
||||
bool OnMessageReceived(const IPC::Message& message) override;
|
||||
|
||||
// WebViewPlugin::Delegate (via PluginPlaceholder) methods:
|
||||
v8::Local<v8::Value> GetV8Handle(v8::Isolate* isolate) override;
|
||||
void ShowContextMenu(const blink::WebMouseEvent&) override;
|
||||
|
||||
// content::RenderProcessObserver methods:
|
||||
void PluginListChanged() override;
|
||||
|
||||
// content::ContextMenuClient methods:
|
||||
void OnMenuAction(int request_id, unsigned action) override;
|
||||
void OnMenuClosed(int request_id) override;
|
||||
|
||||
// Javascript callbacks:
|
||||
// Open chrome://plugins in a new tab.
|
||||
void OpenAboutPluginsCallback();
|
||||
|
||||
CefViewHostMsg_GetPluginInfo_Status status_;
|
||||
|
||||
base::string16 title_;
|
||||
|
||||
bool has_host_;
|
||||
int context_menu_request_id_; // Nonzero when request pending.
|
||||
base::string16 plugin_name_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CefPluginPlaceholder);
|
||||
};
|
||||
|
||||
#endif // CEF_LIBCEF_RENDERER_PLUGINS_CEF_PLUGIN_PLACEHOLDER_H_
|
91
libcef/renderer/plugins/plugin_preroller.cc
Normal file
91
libcef/renderer/plugins/plugin_preroller.cc
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright 2015 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 "libcef/renderer/plugins/plugin_preroller.h"
|
||||
|
||||
#include "libcef/renderer/plugins/cef_plugin_placeholder.h"
|
||||
|
||||
#include "base/base64.h"
|
||||
#include "chrome/grit/generated_resources.h"
|
||||
#include "third_party/WebKit/public/platform/WebRect.h"
|
||||
#include "third_party/WebKit/public/web/WebElement.h"
|
||||
#include "third_party/WebKit/public/web/WebPlugin.h"
|
||||
#include "third_party/WebKit/public/web/WebPluginContainer.h"
|
||||
#include "ui/gfx/codec/png_codec.h"
|
||||
|
||||
CefPluginPreroller::CefPluginPreroller(
|
||||
content::RenderFrame* render_frame,
|
||||
blink::WebLocalFrame* frame,
|
||||
const blink::WebPluginParams& params,
|
||||
const content::WebPluginInfo& info,
|
||||
const std::string& identifier,
|
||||
const base::string16& name,
|
||||
const base::string16& message,
|
||||
content::PluginInstanceThrottler* throttler)
|
||||
: RenderFrameObserver(render_frame),
|
||||
frame_(frame),
|
||||
params_(params),
|
||||
info_(info),
|
||||
identifier_(identifier),
|
||||
name_(name),
|
||||
message_(message),
|
||||
throttler_(throttler) {
|
||||
DCHECK(throttler);
|
||||
throttler_->AddObserver(this);
|
||||
}
|
||||
|
||||
CefPluginPreroller::~CefPluginPreroller() {
|
||||
if (throttler_)
|
||||
throttler_->RemoveObserver(this);
|
||||
}
|
||||
|
||||
void CefPluginPreroller::OnKeyframeExtracted(const SkBitmap* bitmap) {
|
||||
std::vector<unsigned char> png_data;
|
||||
if (!gfx::PNGCodec::EncodeBGRASkBitmap(*bitmap, false, &png_data)) {
|
||||
DLOG(ERROR) << "Provided keyframe could not be encoded as PNG.";
|
||||
return;
|
||||
}
|
||||
|
||||
base::StringPiece png_as_string(reinterpret_cast<char*>(&png_data[0]),
|
||||
png_data.size());
|
||||
|
||||
std::string data_url_header = "data:image/png;base64,";
|
||||
std::string data_url_body;
|
||||
base::Base64Encode(png_as_string, &data_url_body);
|
||||
keyframe_data_url_ = GURL(data_url_header + data_url_body);
|
||||
}
|
||||
|
||||
void CefPluginPreroller::OnThrottleStateChange() {
|
||||
if (!throttler_->IsThrottled())
|
||||
return;
|
||||
|
||||
PlaceholderPosterInfo poster_info;
|
||||
poster_info.poster_attribute = keyframe_data_url_.spec();
|
||||
poster_info.custom_poster_size = throttler_->GetSize();
|
||||
|
||||
CefPluginPlaceholder* placeholder =
|
||||
CefPluginPlaceholder::CreateBlockedPlugin(
|
||||
render_frame(), frame_, params_, info_, identifier_, name_,
|
||||
IDR_PLUGIN_POSTER_HTML, message_, poster_info);
|
||||
placeholder->SetPremadePlugin(throttler_);
|
||||
placeholder->set_power_saver_enabled(true);
|
||||
placeholder->AllowLoading();
|
||||
|
||||
blink::WebPluginContainer* container =
|
||||
throttler_->GetWebPlugin()->container();
|
||||
container->setPlugin(placeholder->plugin());
|
||||
|
||||
bool success = placeholder->plugin()->initialize(container);
|
||||
DCHECK(success);
|
||||
|
||||
container->invalidate();
|
||||
container->reportGeometry();
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CefPluginPreroller::OnThrottlerDestroyed() {
|
||||
throttler_ = nullptr;
|
||||
delete this;
|
||||
}
|
61
libcef/renderer/plugins/plugin_preroller.h
Normal file
61
libcef/renderer/plugins/plugin_preroller.h
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
#ifndef CEF_LIBCEF_RENDERER_PLUGINS_PLUGIN_PREROLLER_H_
|
||||
#define CEF_LIBCEF_RENDERER_PLUGINS_PLUGIN_PREROLLER_H_
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "content/public/common/webplugininfo.h"
|
||||
#include "content/public/renderer/plugin_instance_throttler.h"
|
||||
#include "content/public/renderer/render_frame_observer.h"
|
||||
#include "third_party/WebKit/public/web/WebPluginParams.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace blink {
|
||||
class WebLocalFrame;
|
||||
class WebPlugin;
|
||||
}
|
||||
|
||||
class SkBitmap;
|
||||
|
||||
// This class manages a plugin briefly for the purposes of keyframe extraction.
|
||||
// Once a keyframe has been extracted, this class will replace the plugin with
|
||||
// a ChromePluginPlaceholder. The actual plugin will continue to live in a
|
||||
// throttled state. This class manages its own lifetime.
|
||||
class CefPluginPreroller : public content::PluginInstanceThrottler::Observer,
|
||||
public content::RenderFrameObserver {
|
||||
public:
|
||||
// Does not take ownership of either |plugin| or |throttler|.
|
||||
CefPluginPreroller(content::RenderFrame* render_frame,
|
||||
blink::WebLocalFrame* frame,
|
||||
const blink::WebPluginParams& params,
|
||||
const content::WebPluginInfo& info,
|
||||
const std::string& identifier,
|
||||
const base::string16& name,
|
||||
const base::string16& message,
|
||||
content::PluginInstanceThrottler* throttler);
|
||||
|
||||
~CefPluginPreroller() override;
|
||||
|
||||
private:
|
||||
// content::PluginInstanceThrottler::Observer methods:
|
||||
void OnKeyframeExtracted(const SkBitmap* bitmap) override;
|
||||
void OnThrottleStateChange() override;
|
||||
void OnThrottlerDestroyed() override;
|
||||
|
||||
blink::WebLocalFrame* frame_;
|
||||
blink::WebPluginParams params_;
|
||||
content::WebPluginInfo info_;
|
||||
std::string identifier_;
|
||||
base::string16 name_;
|
||||
base::string16 message_;
|
||||
|
||||
content::PluginInstanceThrottler* throttler_;
|
||||
|
||||
GURL keyframe_data_url_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CefPluginPreroller);
|
||||
};
|
||||
|
||||
#endif // CEF_LIBCEF_RENDERER_PLUGINS_PLUGIN_PREROLLER_H_
|
@@ -20,6 +20,22 @@
|
||||
|
||||
<!-- Manifest files for built-in extensions. -->
|
||||
<include name="IDR_PDF_MANIFEST" file="..\..\..\chrome\browser\resources\pdf\manifest.json" type="BINDATA" />
|
||||
|
||||
<!-- Plugin placeholders. -->
|
||||
<include name="IDR_BLOCKED_PLUGIN_HTML" file="..\..\..\chrome\renderer\resources\plugins\blocked_plugin.html" flattenhtml="true" type="BINDATA" />
|
||||
<include name="IDR_PLUGIN_POSTER_HTML" file="..\..\..\chrome\renderer\resources\plugins\plugin_poster.html" flattenhtml="true" type="BINDATA" />
|
||||
|
||||
<!-- Plugin finder. -->
|
||||
<if expr="is_win">
|
||||
<include name="IDR_PLUGIN_DB_JSON" file="..\..\..\chrome\browser\resources\plugin_metadata\plugins_win.json" type="BINDATA" />
|
||||
</if>
|
||||
<if expr="is_macosx">
|
||||
<include name="IDR_PLUGIN_DB_JSON" file="..\..\..\chrome\browser\resources\plugin_metadata\plugins_mac.json" type="BINDATA" />
|
||||
</if>
|
||||
<if expr="desktop_linux or (is_android and enable_plugins)">
|
||||
<include name="IDR_PLUGIN_DB_JSON" file="..\..\..\chrome\browser\resources\plugin_metadata\plugins_linux.json" type="BINDATA" />
|
||||
</if>
|
||||
|
||||
</includes>
|
||||
</release>
|
||||
</grit>
|
||||
|
@@ -356,6 +356,52 @@ need to be translated for each locale.-->
|
||||
DirectWrite Font Cache Builder
|
||||
</message>
|
||||
</if>
|
||||
|
||||
<!-- Plugin placeholders. -->
|
||||
<message name="IDS_PLUGIN_HIDE" desc="The tooltip for hiding a blocked plugin, displayed in the click to play UI.">
|
||||
Hide this plugin
|
||||
</message>
|
||||
<message name="IDS_PLUGIN_NOT_SUPPORTED" desc="The placeholder text for an unsupported plugin.">
|
||||
This plugin is not supported.
|
||||
</message>
|
||||
<if expr="is_macosx">
|
||||
<message name="IDS_PLUGIN_BLOCKED" desc="The placeholder text for a blocked plugin.">
|
||||
Control-click to play <ph name="PLUGIN_NAME">$1<ex>Flash</ex></ph>.
|
||||
</message>
|
||||
</if>
|
||||
<if expr="not is_macosx">
|
||||
<message name="IDS_PLUGIN_BLOCKED" desc="The placeholder text for a blocked plugin.">
|
||||
Right-click to play <ph name="PLUGIN_NAME">$1<ex>Flash</ex></ph>.
|
||||
</message>
|
||||
</if>
|
||||
<message name="IDS_PLUGIN_NOT_SUPPORTED_METRO" desc="The placeholder text for a plugin that can't run in Windows Metro mode.">
|
||||
This plugin only works on the desktop.
|
||||
</message>
|
||||
<message name="IDS_PLUGIN_OUTDATED" desc="The placeholder text for an outdated plugin.">
|
||||
<ph name="PLUGIN_NAME">$1<ex>Flash</ex></ph> is out of date.
|
||||
</message>
|
||||
<message name="IDS_PLUGIN_NOT_AUTHORIZED" desc="The placeholder text for a plugin that requires user permission to run.">
|
||||
<ph name="PLUGIN_NAME">$1<ex>Java</ex></ph> needs your permission to run.
|
||||
</message>
|
||||
<message name="IDS_PLUGIN_BLOCKED_BY_POLICY" desc="The placeholder text for a plugin blocked by enterprise policy.">
|
||||
<ph name="PLUGIN_NAME">$1<ex>Flash</ex></ph> is not allowed.
|
||||
</message>
|
||||
<if expr="not use_titlecase">
|
||||
<message name="IDS_CONTENT_CONTEXT_PLUGIN_RUN" desc="The name of the Run command on the blocked plugin context menu">
|
||||
Run this plugin
|
||||
</message>
|
||||
<message name="IDS_CONTENT_CONTEXT_PLUGIN_HIDE" desc="The name of the Hide command on the blocked plugin context menu">
|
||||
Hide this plugin
|
||||
</message>
|
||||
</if>
|
||||
<if expr="use_titlecase">
|
||||
<message name="IDS_CONTENT_CONTEXT_PLUGIN_RUN" desc="In Title Case: The name of the Run command on the blocked plugin context menu">
|
||||
Run This Plugin
|
||||
</message>
|
||||
<message name="IDS_CONTENT_CONTEXT_PLUGIN_HIDE" desc="In Title Case: The name of the Hide command on the blocked plugin context menu">
|
||||
Hide This Plugin
|
||||
</message>
|
||||
</if>
|
||||
</messages>
|
||||
</release>
|
||||
</grit>
|
||||
|
Reference in New Issue
Block a user