diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-10 21:16:34 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-10 21:16:34 +0000 |
commit | 2cc0622486b85be1e098ecd2af563c0fa9743b26 (patch) | |
tree | eb32a99ee1dddb5699ee5208bd2b153501020fb3 | |
parent | 82388663bfbeb4dc2fc58e86f83505e67b483d31 (diff) | |
download | chromium_src-2cc0622486b85be1e098ecd2af563c0fa9743b26.zip chromium_src-2cc0622486b85be1e098ecd2af563c0fa9743b26.tar.gz chromium_src-2cc0622486b85be1e098ecd2af563c0fa9743b26.tar.bz2 |
Ensure that PP_Instance values are unique within a plugin process in addition
to within the renderer.
This works by having the renderer check with the plugin that a PP_Instance is
available before using it. If it's already seen, the renderer will generate a
new PP_Instance and retry.
For performance, this message is handled on the I/O thread of the plugin so it
will not be blocked by the actual plugin code.
This requires an unfortunate amount of plumbing. Since the renderer can't
depend directly on the proxy, we have a new PPB_Proxy function to set the
verification function used to perform this check.
There is also a new plugin dispatcher delegate where I moved some of the global
state to that used to go into the init function. Adding yet another parameter
there seemed unfortunate.
TEST=manual
BUG=74961
Review URL: http://codereview.chromium.org/6628019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77693 0039d316-1c4b-4281-b951-d872f2087c98
23 files changed, 339 insertions, 35 deletions
diff --git a/chrome/common/DEPS b/chrome/common/DEPS index cd9bd3f..e6f68bb 100644 --- a/chrome/common/DEPS +++ b/chrome/common/DEPS @@ -6,6 +6,7 @@ include_rules = [ "+media/audio", "+media/base", "+ppapi/c", # For various types. + "+ppapi/proxy", "+remoting/client/plugin", "+sandbox/src", "+skia", diff --git a/chrome/common/pepper_plugin_registry.cc b/chrome/common/pepper_plugin_registry.cc index 2b315fb..ff312a2 100644 --- a/chrome/common/pepper_plugin_registry.cc +++ b/chrome/common/pepper_plugin_registry.cc @@ -13,6 +13,7 @@ #include "base/utf_string_conversions.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" +#include "content/common/child_process.h" #include "content/common/content_switches.h" #include "remoting/client/plugin/pepper_entrypoints.h" @@ -326,3 +327,19 @@ PepperPluginRegistry::PepperPluginRegistry() { preloaded_modules_[current.path] = module; } } + +MessageLoop* PepperPluginRegistry::GetIPCMessageLoop() { + // This is called only in the renderer so we know we have a child process. + DCHECK(ChildProcess::current()) << "Must be in the renderer."; + return ChildProcess::current()->io_message_loop(); +} + +base::WaitableEvent* PepperPluginRegistry::GetShutdownEvent() { + DCHECK(ChildProcess::current()) << "Must be in the renderer."; + return ChildProcess::current()->GetShutDownEvent(); +} + +std::set<PP_Instance>* PepperPluginRegistry::GetGloballySeenInstanceIDSet() { + // This function is not needed on the host side of the proxy. + return NULL; +} diff --git a/chrome/common/pepper_plugin_registry.h b/chrome/common/pepper_plugin_registry.h index beb808c3..1852954 100644 --- a/chrome/common/pepper_plugin_registry.h +++ b/chrome/common/pepper_plugin_registry.h @@ -11,6 +11,7 @@ #include <vector> #include "base/file_path.h" +#include "ppapi/proxy/dispatcher.h" #include "webkit/plugins/npapi/webplugininfo.h" #include "webkit/plugins/ppapi/plugin_delegate.h" #include "webkit/plugins/ppapi/plugin_module.h" @@ -45,7 +46,8 @@ struct PepperPluginInfo { // is a list of all live modules (some of which may be out-of-process and hence // not preloaded). class PepperPluginRegistry - : public webkit::ppapi::PluginDelegate::ModuleLifetime { + : public webkit::ppapi::PluginDelegate::ModuleLifetime, + public pp::proxy::Dispatcher::Delegate { public: ~PepperPluginRegistry(); @@ -90,6 +92,11 @@ class PepperPluginRegistry private: PepperPluginRegistry(); + // Dispatcher::Delegate implementation. + virtual MessageLoop* GetIPCMessageLoop(); + virtual base::WaitableEvent* GetShutdownEvent(); + virtual std::set<PP_Instance>* GetGloballySeenInstanceIDSet(); + // All known pepper plugins. std::vector<PepperPluginInfo> plugin_list_; diff --git a/chrome/ppapi_plugin/ppapi_thread.cc b/chrome/ppapi_plugin/ppapi_thread.cc index 98d0014..4e998ea 100644 --- a/chrome/ppapi_plugin/ppapi_thread.cc +++ b/chrome/ppapi_plugin/ppapi_thread.cc @@ -48,6 +48,18 @@ bool PpapiThread::OnMessageReceived(const IPC::Message& msg) { return true; } +MessageLoop* PpapiThread::GetIPCMessageLoop() { + return ChildProcess::current()->io_message_loop(); +} + +base::WaitableEvent* PpapiThread::GetShutdownEvent() { + return ChildProcess::current()->GetShutDownEvent(); +} + +std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() { + return &globally_seen_instance_ids_; +} + void PpapiThread::OnMsgLoadPlugin(const FilePath& path) { base::ScopedNativeLibrary library(base::LoadNativeLibrary(path)); if (!library.is_valid()) @@ -103,10 +115,7 @@ bool PpapiThread::SetupRendererChannel(base::ProcessHandle host_process_handle, IPC::ChannelHandle plugin_handle; plugin_handle.name = StringPrintf("%d.r%d", base::GetCurrentProcId(), renderer_id); - if (!dispatcher->InitWithChannel( - ChildProcess::current()->io_message_loop(), - plugin_handle, false, - ChildProcess::current()->GetShutDownEvent())) + if (!dispatcher->InitWithChannel(this, plugin_handle, false)) return false; handle->name = plugin_handle.name; diff --git a/chrome/ppapi_plugin/ppapi_thread.h b/chrome/ppapi_plugin/ppapi_thread.h index e4058ad..34f06eb 100644 --- a/chrome/ppapi_plugin/ppapi_thread.h +++ b/chrome/ppapi_plugin/ppapi_thread.h @@ -27,7 +27,8 @@ class PluginDispatcher; } } -class PpapiThread : public ChildThread { +class PpapiThread : public ChildThread, + public pp::proxy::Dispatcher::Delegate { public: PpapiThread(); ~PpapiThread(); @@ -36,6 +37,11 @@ class PpapiThread : public ChildThread { // ChildThread overrides. virtual bool OnMessageReceived(const IPC::Message& msg); + // Dispatcher::Delegate implementation. + virtual MessageLoop* GetIPCMessageLoop(); + virtual base::WaitableEvent* GetShutdownEvent(); + virtual std::set<PP_Instance>* GetGloballySeenInstanceIDSet(); + // Message handlers. void OnMsgLoadPlugin(const FilePath& path); void OnMsgCreateChannel(base::ProcessHandle host_process_handle, @@ -59,6 +65,9 @@ class PpapiThread : public ChildThread { // and pass it around as necessary. PP_Module local_pp_module_; + // See Dispatcher::Delegate::GetGloballySeenInstanceIDSet. + std::set<PP_Instance> globally_seen_instance_ids_; + DISALLOW_COPY_AND_ASSIGN(PpapiThread); }; diff --git a/chrome/renderer/pepper_plugin_delegate_impl.cc b/chrome/renderer/pepper_plugin_delegate_impl.cc index 2fa07c2..9ba59c8 100644 --- a/chrome/renderer/pepper_plugin_delegate_impl.cc +++ b/chrome/renderer/pepper_plugin_delegate_impl.cc @@ -362,9 +362,8 @@ bool DispatcherWrapper::Init( dispatcher_.reset(new pp::proxy::HostDispatcher( plugin_process_handle, pp_module, local_get_interface)); - if (!dispatcher_->InitWithChannel( - ChildProcess::current()->io_message_loop(), channel_handle, - true, ChildProcess::current()->GetShutDownEvent())) { + if (!dispatcher_->InitWithChannel(PepperPluginRegistry::GetInstance(), + channel_handle, true)) { dispatcher_.reset(); return false; } diff --git a/content/common/child_process.h b/content/common/child_process.h index 518314d..d7e9a00 100644 --- a/content/common/child_process.h +++ b/content/common/child_process.h @@ -18,11 +18,15 @@ class ChildThread; class ChildProcess { public: // Child processes should have an object that derives from this class. + // Normally you would immediately call set_main_thread after construction. ChildProcess(); virtual ~ChildProcess(); - // Getter for the child process' main thread. + // May be NULL if the main thread hasn't been set explicitly. ChildThread* main_thread(); + + // Sets the object associated with the main thread of this process. + // Takes ownership of the pointer. void set_main_thread(ChildThread* thread); MessageLoop* io_message_loop() { return io_thread_.message_loop(); } diff --git a/ppapi/c/private/ppb_proxy_private.h b/ppapi/c/private/ppb_proxy_private.h index d003281..ae6a39e 100644 --- a/ppapi/c/private/ppb_proxy_private.h +++ b/ppapi/c/private/ppb_proxy_private.h @@ -5,11 +5,12 @@ #ifndef PPAPI_C_PRIVATE_PROXY_PRIVATE_H_ #define PPAPI_C_PRIVATE_PROXY_PRIVATE_H_ +#include "ppapi/c/pp_bool.h" #include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_module.h" #include "ppapi/c/pp_resource.h" -#define PPB_PROXY_PRIVATE_INTERFACE "PPB_Proxy_Private;2" +#define PPB_PROXY_PRIVATE_INTERFACE "PPB_Proxy_Private;3" // Exposes functions needed by the out-of-process proxy to call into the // renderer PPAPI implementation. @@ -19,6 +20,19 @@ struct PPB_Proxy_Private { // Returns the instance for the given resource, or 0 on failure. PP_Instance (*GetInstanceForResource)(PP_Resource resource); + + // Sets a callback that will be used to make sure that PP_Instance IDs + // are unique in the plugin. + // + // Since the plugin may be shared between several browser processes, we need + // to do extra work to make sure that an instance ID is globally unqiue. The + // given function will be called and will return true if the given + // PP_Instance is OK to use in the plugin. It will then be marked as "in use" + // On failure (returns false), the host implementation will generate a new + // instance ID and try again. + void (*SetReserveInstanceIDCallback)( + PP_Module module, + PP_Bool (*is_seen)(PP_Module, PP_Instance)); }; #endif // PPAPI_C_PRIVATE_PROXY_PRIVATE_H_ diff --git a/ppapi/ppapi_shared_proxy.gypi b/ppapi/ppapi_shared_proxy.gypi index 0c8b1ce..8c8a047 100644 --- a/ppapi/ppapi_shared_proxy.gypi +++ b/ppapi/ppapi_shared_proxy.gypi @@ -58,6 +58,8 @@ 'proxy/interface_proxy.h', 'proxy/plugin_dispatcher.cc', 'proxy/plugin_dispatcher.h', + 'proxy/plugin_message_filter.cc', + 'proxy/plugin_message_filter.h', 'proxy/plugin_resource.cc', 'proxy/plugin_resource.h', 'proxy/plugin_resource_tracker.cc', diff --git a/ppapi/proxy/dispatcher.cc b/ppapi/proxy/dispatcher.cc index dd4167c..391a882 100644 --- a/ppapi/proxy/dispatcher.cc +++ b/ppapi/proxy/dispatcher.cc @@ -165,7 +165,8 @@ InterfaceList* InterfaceList::GetInstance() { Dispatcher::Dispatcher(base::ProcessHandle remote_process_handle, GetInterfaceFunc local_get_interface) - : remote_process_handle_(remote_process_handle), + : delegate_(NULL), + remote_process_handle_(remote_process_handle), test_sink_(NULL), disallow_trusted_interfaces_(false), // TODO(brettw) make this settable. local_get_interface_(local_get_interface), @@ -175,14 +176,14 @@ Dispatcher::Dispatcher(base::ProcessHandle remote_process_handle, Dispatcher::~Dispatcher() { } -bool Dispatcher::InitWithChannel(MessageLoop* ipc_message_loop, +bool Dispatcher::InitWithChannel(Delegate* delegate, const IPC::ChannelHandle& channel_handle, - bool is_client, - base::WaitableEvent* shutdown_event) { + bool is_client) { IPC::Channel::Mode mode = is_client ? IPC::Channel::MODE_CLIENT : IPC::Channel::MODE_SERVER; channel_.reset(new IPC::SyncChannel(channel_handle, mode, this, - ipc_message_loop, false, shutdown_event)); + delegate->GetIPCMessageLoop(), false, + delegate->GetShutdownEvent())); return true; } diff --git a/ppapi/proxy/dispatcher.h b/ppapi/proxy/dispatcher.h index 161aa54..edcb7e7 100644 --- a/ppapi/proxy/dispatcher.h +++ b/ppapi/proxy/dispatcher.h @@ -6,6 +6,7 @@ #define PPAPI_PROXY_DISPATCHER_H_ #include <map> +#include <set> #include <string> #include <vector> @@ -16,6 +17,7 @@ #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_message.h" #include "ipc/ipc_platform_file.h" +#include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_module.h" #include "ppapi/proxy/callback_tracker.h" #include "ppapi/proxy/interface_id.h" @@ -60,13 +62,31 @@ class Dispatcher : public IPC::Channel::Listener, typedef int32_t (*InitModuleFunc)(PP_Module, GetInterfaceFunc); typedef void (*ShutdownModuleFunc)(); + class Delegate { + public: + // Returns the dedicated message loop for processing IPC requests. + virtual MessageLoop* GetIPCMessageLoop() = 0; + + // Returns the event object that becomes signalled when the main thread's + // message loop exits. + virtual base::WaitableEvent* GetShutdownEvent() = 0; + + // Returns the set used for globally uniquifying PP_Instances. This same + // set must be returned for all channels. This is required only for the + // plugin side, for the host side, the return value may be NULL. + // + // DEREFERENCE ONLY ON THE I/O THREAD. + virtual std::set<PP_Instance>* GetGloballySeenInstanceIDSet() = 0; + }; + virtual ~Dispatcher(); // You must call this function before anything else. Returns true on success. - bool InitWithChannel(MessageLoop* ipc_message_loop, - const IPC::ChannelHandle& channel_handle, - bool is_client, - base::WaitableEvent* shutdown_event); + // The delegate pointer must outlive this class, ownership is not + // transferred. + virtual bool InitWithChannel(Delegate* delegate, + const IPC::ChannelHandle& channel_handle, + bool is_client); // Alternative to InitWithChannel() for unit tests that want to send all // messages sent via this dispatcher to the given test sink. The test sink @@ -137,7 +157,12 @@ class Dispatcher : public IPC::Channel::Listener, return disallow_trusted_interfaces_; } + Delegate* delegate() { return delegate_; } + private: + // Non-owning pointer. Guaranteed non-NULL after init is called. + Delegate* delegate_; + base::ProcessHandle remote_process_handle_; // See getter above. // When we're unit testing, this will indicate the sink for the messages to diff --git a/ppapi/proxy/host_dispatcher.cc b/ppapi/proxy/host_dispatcher.cc index aa972b5..52305e0 100644 --- a/ppapi/proxy/host_dispatcher.cc +++ b/ppapi/proxy/host_dispatcher.cc @@ -20,6 +20,27 @@ namespace { typedef std::map<PP_Instance, HostDispatcher*> InstanceToDispatcherMap; InstanceToDispatcherMap* g_instance_to_dispatcher = NULL; +typedef std::map<PP_Module, HostDispatcher*> ModuleToDispatcherMap; +ModuleToDispatcherMap* g_module_to_dispatcher = NULL; + +PP_Bool ReserveInstanceID(PP_Module module, PP_Instance instance) { + // Default to returning true (usable) failure. Otherwise, if there's some + // kind of communication error or the plugin just crashed, we'll get into an + // infinite loop generating new instnace IDs since we think they're all in + // use. + ModuleToDispatcherMap::const_iterator found = + g_module_to_dispatcher->find(module); + if (found == g_module_to_dispatcher->end()) { + NOTREACHED(); + return PP_TRUE; + } + + bool usable = true; + if (!found->second->Send(new PpapiMsg_ReserveInstanceId(instance, &usable))) + return PP_TRUE; + return BoolToPPBool(usable); +} + } // namespace HostDispatcher::HostDispatcher(base::ProcessHandle remote_process_handle, @@ -28,6 +49,10 @@ HostDispatcher::HostDispatcher(base::ProcessHandle remote_process_handle, : Dispatcher(remote_process_handle, local_get_interface), pp_module_(module), ppb_proxy_(NULL) { + if (!g_module_to_dispatcher) + g_module_to_dispatcher = new ModuleToDispatcherMap; + (*g_module_to_dispatcher)[pp_module_] = this; + const PPB_Var_Deprecated* var_interface = static_cast<const PPB_Var_Deprecated*>( local_get_interface(PPB_VAR_DEPRECATED_INTERFACE)); @@ -35,9 +60,16 @@ HostDispatcher::HostDispatcher(base::ProcessHandle remote_process_handle, memset(plugin_interface_support_, 0, sizeof(PluginInterfaceSupport) * INTERFACE_ID_COUNT); + + ppb_proxy_ = reinterpret_cast<const PPB_Proxy_Private*>( + GetLocalInterface(PPB_PROXY_PRIVATE_INTERFACE)); + DCHECK(ppb_proxy_) << "The proxy interface should always be supported."; + + ppb_proxy_->SetReserveInstanceIDCallback(pp_module_, &ReserveInstanceID); } HostDispatcher::~HostDispatcher() { + g_module_to_dispatcher->erase(pp_module_); } // static @@ -104,7 +136,7 @@ void HostDispatcher::OnChannelError() { Dispatcher::OnChannelError(); // Stop using the channel. // Tell the host about the crash so it can clean up. - GetPPBProxy()->PluginCrashed(pp_module()); + ppb_proxy_->PluginCrashed(pp_module()); } const void* HostDispatcher::GetProxiedInterface(const std::string& interface) { @@ -152,14 +184,6 @@ InterfaceProxy* HostDispatcher::GetOrCreatePPBInterfaceProxy( return proxy; } -const PPB_Proxy_Private* HostDispatcher::GetPPBProxy() { - if (!ppb_proxy_) { - ppb_proxy_ = reinterpret_cast<const PPB_Proxy_Private*>( - GetLocalInterface(PPB_PROXY_PRIVATE_INTERFACE)); - } - return ppb_proxy_; -} - InterfaceProxy* HostDispatcher::CreatePPBInterfaceProxy( const InterfaceProxy::Info* info) { const void* local_interface = GetLocalInterface(info->name); diff --git a/ppapi/proxy/host_dispatcher.h b/ppapi/proxy/host_dispatcher.h index 35cdece..0fed9bd 100644 --- a/ppapi/proxy/host_dispatcher.h +++ b/ppapi/proxy/host_dispatcher.h @@ -79,7 +79,7 @@ class HostDispatcher : public Dispatcher { InterfaceProxy* GetOrCreatePPBInterfaceProxy(InterfaceID id); // Returns the proxy interface for talking to the implementation. - const PPB_Proxy_Private* GetPPBProxy(); + const PPB_Proxy_Private* ppb_proxy() const { return ppb_proxy_; } private: friend class HostDispatcherTest; @@ -101,7 +101,7 @@ class HostDispatcher : public Dispatcher { // messages. They are created on demand when we receive messages. scoped_ptr<InterfaceProxy> target_proxies_[INTERFACE_ID_COUNT]; - // Lazily initialized, may be NULL. Use GetPPBProxy(). + // Guaranteed non-NULL. const PPB_Proxy_Private* ppb_proxy_; DISALLOW_COPY_AND_ASSIGN(HostDispatcher); diff --git a/ppapi/proxy/plugin_dispatcher.cc b/ppapi/proxy/plugin_dispatcher.cc index 645f92f..793ccb8 100644 --- a/ppapi/proxy/plugin_dispatcher.cc +++ b/ppapi/proxy/plugin_dispatcher.cc @@ -12,6 +12,7 @@ #include "ipc/ipc_sync_channel.h" #include "ppapi/c/pp_errors.h" #include "ppapi/proxy/interface_proxy.h" +#include "ppapi/proxy/plugin_message_filter.h" #include "ppapi/proxy/plugin_var_serialization_rules.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/ppp_class_proxy.h" @@ -72,6 +73,20 @@ const void* PluginDispatcher::GetInterfaceFromDispatcher( return info->interface; } +bool PluginDispatcher::InitWithChannel( + Delegate* delegate, + const IPC::ChannelHandle& channel_handle, + bool is_client) { + if (!Dispatcher::InitWithChannel(delegate, channel_handle, is_client)) + return false; + + // The message filter will intercept and process certain messages directly + // on the I/O thread. + channel()->AddFilter( + new PluginMessageFilter(delegate->GetGloballySeenInstanceIDSet())); + return true; +} + bool PluginDispatcher::IsPlugin() const { return true; } diff --git a/ppapi/proxy/plugin_dispatcher.h b/ppapi/proxy/plugin_dispatcher.h index 994edc0..8469ed7 100644 --- a/ppapi/proxy/plugin_dispatcher.h +++ b/ppapi/proxy/plugin_dispatcher.h @@ -49,6 +49,9 @@ class PluginDispatcher : public Dispatcher { static const void* GetInterfaceFromDispatcher(const char* interface); // Dispatcher overrides. + virtual bool InitWithChannel(Delegate* delegate, + const IPC::ChannelHandle& channel_handle, + bool is_client); virtual bool IsPlugin() const; // IPC::Channel::Listener implementation. diff --git a/ppapi/proxy/plugin_message_filter.cc b/ppapi/proxy/plugin_message_filter.cc new file mode 100644 index 0000000..9df2208 --- /dev/null +++ b/ppapi/proxy/plugin_message_filter.cc @@ -0,0 +1,61 @@ +// Copyright (c) 2011 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 "ppapi/proxy/plugin_message_filter.h" + +#include "ppapi/proxy/ppapi_messages.h" + +namespace pp { +namespace proxy { + +PluginMessageFilter::PluginMessageFilter( + std::set<PP_Instance>* seen_instance_ids) + : seen_instance_ids_(seen_instance_ids), + channel_(NULL) { +} + +PluginMessageFilter::~PluginMessageFilter() { +} + +void PluginMessageFilter::OnFilterAdded(IPC::Channel* channel) { + channel_ = channel; +} + +void PluginMessageFilter::OnFilterRemoved() { + channel_ = NULL; +} + +bool PluginMessageFilter::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PluginMessageFilter, message) + IPC_MESSAGE_HANDLER(PpapiMsg_ReserveInstanceId, OnMsgReserveInstanceId) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +bool PluginMessageFilter::Send(IPC::Message* msg) { + if (channel_) + return channel_->Send(msg); + delete msg; + return false; +} + +void PluginMessageFilter::OnMsgReserveInstanceId(PP_Instance instance, + bool* usable) { + // See the message definition for how this works. + if (seen_instance_ids_->find(instance) != seen_instance_ids_->end()) { + // Instance ID already seen, reject it. + *usable = false; + return; + } + + // This instance ID is new so we can return that it's usable and mark it as + // used for future reference. + seen_instance_ids_->insert(instance); + *usable = true; +} + +} // namespace proxy +} // namespace pp diff --git a/ppapi/proxy/plugin_message_filter.h b/ppapi/proxy/plugin_message_filter.h new file mode 100644 index 0000000..fa3b2f4 --- /dev/null +++ b/ppapi/proxy/plugin_message_filter.h @@ -0,0 +1,56 @@ +// Copyright (c) 2011 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 PPAPI_PROXY_PLUGIN_MESSAGE_FILTER_H_ +#define PPAPI_PROXY_PLUGIN_MESSAGE_FILTER_H_ + +#include <set> + +#include "ipc/ipc_channel_proxy.h" +#include "ipc/ipc_message.h" +#include "ppapi/c/pp_instance.h" + +namespace pp { +namespace proxy { + +// Listens for messages on the I/O thread of the plugin and handles some of +// them to avoid needing to block on the plugin. +// +// There is one instance of this class for each renderer channel (same as for +// the PluginDispatchers). +class PluginMessageFilter : public IPC::ChannelProxy::MessageFilter, + public IPC::Message::Sender { + public: + // The input is a pointer to a set that will be used to uniquify PP_Instances + // across all renderer channels. The same pointer should be passed to each + // MessageFilter to ensure uniqueness, and the value should outlive this + // class. + PluginMessageFilter(std::set<PP_Instance>* seen_instance_ids); + virtual ~PluginMessageFilter(); + + // MessageFilter implementation. + virtual void OnFilterAdded(IPC::Channel* channel); + virtual void OnFilterRemoved(); + virtual bool OnMessageReceived(const IPC::Message& message); + + // Message::Sender implementation. + virtual bool Send(IPC::Message* msg); + + private: + void OnMsgReserveInstanceId(PP_Instance instance, bool* usable); + + // All instance IDs every queried by any renderer on this plugin. This is + // used to make sure that new instance IDs are unique. This is a non-owning + // pointer, it will be managed by the later that creates this class. + std::set<PP_Instance>* seen_instance_ids_; + + // The IPC channel to the renderer. May be NULL if we're not currently + // attached as a filter. + IPC::Channel* channel_; +}; + +} // namespace proxy +} // namespace pp + +#endif // PPAPI_PROXY_PLUGIN_MESSAGE_FILTER_H_ diff --git a/ppapi/proxy/ppapi_messages_internal.h b/ppapi/proxy/ppapi_messages_internal.h index e37f65b..375e7d5 100644 --- a/ppapi/proxy/ppapi_messages_internal.h +++ b/ppapi/proxy/ppapi_messages_internal.h @@ -20,6 +20,24 @@ IPC_MESSAGE_CONTROL2(PpapiMsg_CreateChannel, base::ProcessHandle /* host_process_handle */, int /* renderer_id */); +// Each plugin may be referenced by multiple renderers. We need the instance +// IDs to be unique within a plugin, despite coming from different renderers, +// and unique within a renderer, despite going to different plugins. This means +// that neither the renderer nor the plugin can generate instance IDs without +// consulting the other. +// +// We resolve this by having the renderer generate a unique instance ID inside +// its process. It then asks the plugin to reserve that ID by sending this sync +// message. If the plugin has not yet seen this ID, it will remember it as used +// (to prevent a race condition if another renderer tries to then use the same +// instance), and set usable as true. +// +// If the plugin has already seen the instance ID, it will set usable as false +// and the renderer must retry a new instance ID. +IPC_SYNC_MESSAGE_CONTROL1_1(PpapiMsg_ReserveInstanceId, + PP_Instance /* instance */, + bool /* usable */) + // Sent in both directions to see if the other side supports the given // interface. IPC_SYNC_MESSAGE_CONTROL1_1(PpapiMsg_SupportsInterface, diff --git a/ppapi/proxy/ppb_file_ref_proxy.cc b/ppapi/proxy/ppb_file_ref_proxy.cc index 3777ee3..ddf29e4 100644 --- a/ppapi/proxy/ppb_file_ref_proxy.cc +++ b/ppapi/proxy/ppb_file_ref_proxy.cc @@ -259,7 +259,7 @@ void PPB_FileRef_Proxy::SerializeFileRef(PP_Resource file_ref, } HostDispatcher* host_dispatcher = static_cast<HostDispatcher*>(dispatcher()); PP_Instance instance = - host_dispatcher->GetPPBProxy()->GetInstanceForResource(file_ref); + host_dispatcher->ppb_proxy()->GetInstanceForResource(file_ref); result->resource.SetHostResource(instance, file_ref); result->file_system_type = diff --git a/webkit/plugins/ppapi/plugin_module.cc b/webkit/plugins/ppapi/plugin_module.cc index 6c670f9..0ea2859 100644 --- a/webkit/plugins/ppapi/plugin_module.cc +++ b/webkit/plugins/ppapi/plugin_module.cc @@ -382,7 +382,8 @@ PluginModule::PluginModule(const std::string& name, callback_tracker_(new CallbackTracker), is_crashed_(false), library_(NULL), - name_(name) { + name_(name), + reserve_instance_id_(NULL) { pp_module_ = ResourceTracker::Get()->AddModule(this); GetMainThreadMessageLoop(); // Initialize the main thread message loop. GetLivePluginSet()->insert(this); @@ -505,6 +506,18 @@ void PluginModule::PluginCrashed() { lifetime_delegate_->PluginModuleDead(this); } +void PluginModule::SetReserveInstanceIDCallback( + PP_Bool (*reserve)(PP_Module, PP_Instance)) { + DCHECK(!reserve_instance_id_) << "Only expect one set."; + reserve_instance_id_ = reserve; +} + +bool PluginModule::ReserveInstanceID(PP_Instance instance) { + if (reserve_instance_id_) + return PPBoolToBool(reserve_instance_id_(pp_module_, instance)); + return true; // Instance ID is usable. +} + bool PluginModule::InitializeModule() { DCHECK(!out_of_process_proxy_.get()) << "Don't call for proxied modules."; int retval = entry_points_.initialize_module(pp_module(), &GetInterface); diff --git a/webkit/plugins/ppapi/plugin_module.h b/webkit/plugins/ppapi/plugin_module.h index 39aa878..45545c9 100644 --- a/webkit/plugins/ppapi/plugin_module.h +++ b/webkit/plugins/ppapi/plugin_module.h @@ -15,6 +15,8 @@ #include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "base/weak_ptr.h" +#include "ppapi/c/pp_bool.h" +#include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_module.h" #include "ppapi/c/ppb.h" #include "webkit/plugins/ppapi/plugin_delegate.h" @@ -126,6 +128,18 @@ class PluginModule : public base::RefCounted<PluginModule>, // release relevant resources and update all affected instances. void PluginCrashed(); + // Reserves the given instance is unique within the plugin, checking for + // collisions. See PPB_Proxy_Private for more information. + // + // The setter will set the callback which is set up when the proxy + // initializes. The Reserve function will call the previously set callback if + // it exists to validate the ID. If the callback has not been set (such as + // for in-process plugins), the Reserve function will assume that the ID is + // usable and will return true. + void SetReserveInstanceIDCallback( + PP_Bool (*reserve)(PP_Module, PP_Instance)); + bool ReserveInstanceID(PP_Instance instance); + private: // Calls the InitializeModule entrypoint. The entrypoint must have been // set and the plugin must not be out of process (we don't maintain @@ -167,6 +181,8 @@ class PluginModule : public base::RefCounted<PluginModule>, typedef std::set<PluginInstance*> PluginInstanceSet; PluginInstanceSet instances_; + PP_Bool (*reserve_instance_id_)(PP_Module, PP_Instance); + DISALLOW_COPY_AND_ASSIGN(PluginModule); }; diff --git a/webkit/plugins/ppapi/ppb_proxy_impl.cc b/webkit/plugins/ppapi/ppb_proxy_impl.cc index 540835e..f82ec46 100644 --- a/webkit/plugins/ppapi/ppb_proxy_impl.cc +++ b/webkit/plugins/ppapi/ppb_proxy_impl.cc @@ -28,9 +28,17 @@ PP_Instance GetInstanceForResource(PP_Resource resource) { return obj->instance()->pp_instance(); } +void SetReserveInstanceIDCallback(PP_Module module, + PP_Bool (*reserve)(PP_Module, PP_Instance)) { + PluginModule* plugin_module = ResourceTracker::Get()->GetModule(module); + if (plugin_module) + plugin_module->SetReserveInstanceIDCallback(reserve); +} + const PPB_Proxy_Private ppb_proxy = { &PluginCrashed, - &GetInstanceForResource + &GetInstanceForResource, + &SetReserveInstanceIDCallback }; } // namespace diff --git a/webkit/plugins/ppapi/resource_tracker.cc b/webkit/plugins/ppapi/resource_tracker.cc index b17210c..79a0125 100644 --- a/webkit/plugins/ppapi/resource_tracker.cc +++ b/webkit/plugins/ppapi/resource_tracker.cc @@ -12,6 +12,7 @@ #include "base/rand_util.h" #include "ppapi/c/pp_resource.h" #include "ppapi/c/pp_var.h" +#include "webkit/plugins/ppapi/plugin_module.h" #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" #include "webkit/plugins/ppapi/resource.h" #include "webkit/plugins/ppapi/var.h" @@ -279,7 +280,8 @@ PP_Instance ResourceTracker::AddInstance(PluginInstance* instance) { new_instance = MakeTypedId(static_cast<PP_Instance>(base::RandUint64()), PP_ID_TYPE_INSTANCE); } while (!new_instance || - instance_map_.find(new_instance) != instance_map_.end()); + instance_map_.find(new_instance) != instance_map_.end() || + !instance->module()->ReserveInstanceID(new_instance)); instance_map_[new_instance].instance = instance; return new_instance; |