diff options
author | mpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-02 23:56:11 +0000 |
---|---|---|
committer | mpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-02 23:56:11 +0000 |
commit | 75e5a879a500897b2409c49d4ef205e2e954c9fd (patch) | |
tree | 166ca53e8cc29520402efd7cc6ee2804edf744eb /chrome/browser/extensions | |
parent | 720ac73783ea732c130ff6d11dffa41919a25809 (diff) | |
download | chromium_src-75e5a879a500897b2409c49d4ef205e2e954c9fd.zip chromium_src-75e5a879a500897b2409c49d4ef205e2e954c9fd.tar.gz chromium_src-75e5a879a500897b2409c49d4ef205e2e954c9fd.tar.bz2 |
Add code to support 2-way communication between extensions and renderers. The code is almost fully symmetrical, except that right now a channel can only be opened to an extension (by ID). It should be trivial to open a channel to a tab, once we have a solid tab API.
Review URL: http://codereview.chromium.org/56037
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13057 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions')
-rwxr-xr-x | chrome/browser/extensions/extension_message_service.cc | 154 | ||||
-rwxr-xr-x | chrome/browser/extensions/extension_message_service.h | 65 | ||||
-rwxr-xr-x | chrome/browser/extensions/extension_view.cc | 6 |
3 files changed, 119 insertions, 106 deletions
diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc index 95143ce..87469f9 100755 --- a/chrome/browser/extensions/extension_message_service.cc +++ b/chrome/browser/extensions/extension_message_service.cc @@ -13,121 +13,117 @@ #include "chrome/browser/renderer_host/resource_message_filter.h" #include "chrome/common/render_messages.h" -// This class acts as the port to an extension process. It is basically just -// gymnastics to get access to the IPC::Channel (not the ChannelProxy) belonging -// to an ExtensionView. -// Created on the UI thread, but accessed fully on the IO thread. -class ExtensionMessageService::ExtensionFilter : - public IPC::ChannelProxy::MessageFilter { - public: - ExtensionFilter(const std::string& extension_id, int routing_id) : - extension_id_(extension_id), routing_id_(routing_id), channel_(NULL) { - } - ~ExtensionFilter() { - ExtensionMessageService::GetInstance()->OnExtensionUnregistered(this); - } - - virtual void OnFilterAdded(IPC::Channel* channel) { - channel_ = channel; - ExtensionMessageService::GetInstance()->OnExtensionRegistered(this); - } - virtual void OnChannelClosing() { - channel_ = NULL; - } - - bool Send(IPC::Message* message) { - if (!channel_) { - delete message; - return false; - } - return channel_->Send(message); - } +// Since we have 2 ports for every channel, we just index channels by half the +// port ID. +#define GET_CHANNEL_ID(port_id) (port_id / 2) - IPC::Channel* channel() { return channel_; } - const std::string& extension_id() { return extension_id_; } - int routing_id() { return routing_id_; } +// Port1 is always even, port2 is always odd. +#define IS_PORT1_ID(port_id) ((port_id & 1) == 0) - private: - std::string extension_id_; - int routing_id_; - IPC::Channel* channel_; -}; +// Change even to odd and vice versa, to get the other side of a given channel. +#define GET_OPPOSITE_PORT_ID(source_port_id) (source_port_id ^ 1) ExtensionMessageService* ExtensionMessageService::GetInstance() { return Singleton<ExtensionMessageService>::get(); } ExtensionMessageService::ExtensionMessageService() - : next_channel_id_(0) { + : next_port_id_(0) { } -void ExtensionMessageService::RegisterExtensionView(ExtensionView* view) { - view->render_view_host()->process()->channel()->AddFilter( - new ExtensionFilter(view->extension()->id(), - view->render_view_host()->routing_id())); -} - -void ExtensionMessageService::OnExtensionRegistered(ExtensionFilter* filter) { - extensions_[filter->extension_id()] = filter; -} - -void ExtensionMessageService::OnExtensionUnregistered(ExtensionFilter* filter) { - // TODO(mpcomplete): support multiple filters per extension_id - //DCHECK(extensions_[filter->extension_id()] == filter); - extensions_.erase(filter->extension_id()); - - // Close any channels that share this filter. - for (MessageChannelMap::iterator it = channels_.begin(); - it != channels_.end(); ) { - MessageChannelMap::iterator current = it++; - if (current->second.extension_port == filter) - channels_.erase(current); - } +void ExtensionMessageService::RegisterExtension( + const std::string& extension_id, int render_process_id) { + AutoLock lock(renderers_lock_); + // TODO(mpcomplete): We need to ensure an extension always ends up in a single + // process. I think this means having an ExtensionProcessManager which holds + // a BrowsingContext for each extension. + //DCHECK(process_ids_.find(extension_id) == process_ids_.end()); + process_ids_[extension_id] = render_process_id; } int ExtensionMessageService::OpenChannelToExtension( - const std::string& extension_id, ResourceMessageFilter* renderer_port) { + const std::string& extension_id, ResourceMessageFilter* source) { DCHECK(MessageLoop::current() == ChromeThread::GetMessageLoop(ChromeThread::IO)); - ExtensionMap::iterator extension_port = extensions_.find(extension_id); - if (extension_port == extensions_.end()) - return -1; + // Lookup the targeted extension process. + ResourceMessageFilter* dest = NULL; + { + AutoLock lock(renderers_lock_); + ProcessIDMap::iterator process_id = process_ids_.find(extension_id); + if (process_id == process_ids_.end()) + return -1; + + RendererMap::iterator renderer = renderers_.find(process_id->second); + if (renderer == renderers_.end()) + return -1; + dest = renderer->second; + } + + // Create a channel ID for both sides of the channel. + int port1_id = next_port_id_++; + int port2_id = next_port_id_++; + DCHECK(IS_PORT1_ID(port1_id)); + DCHECK(GET_OPPOSITE_PORT_ID(port1_id) == port2_id); + DCHECK(GET_OPPOSITE_PORT_ID(port2_id) == port1_id); + DCHECK(GET_CHANNEL_ID(port1_id) == GET_CHANNEL_ID(port2_id)); - int channel_id = next_channel_id_++; MessageChannel channel; - channel.renderer_port = renderer_port; - channel.extension_port = extension_port->second; - channels_[channel_id] = channel; + channel.port1 = source; + channel.port2 = dest; + channels_[GET_CHANNEL_ID(port1_id)] = channel; - return channel_id; + // Send each process the id for the opposite port. + dest->Send(new ViewMsg_ExtensionHandleConnect(port1_id)); + return port2_id; } -void ExtensionMessageService::PostMessageToExtension( - int channel_id, const std::string& message) { +void ExtensionMessageService::PostMessageFromRenderer( + int port_id, const std::string& message, ResourceMessageFilter* source) { DCHECK(MessageLoop::current() == ChromeThread::GetMessageLoop(ChromeThread::IO)); - MessageChannelMap::iterator iter = channels_.find(channel_id); + // Look up the channel by port1's ID. + MessageChannelMap::iterator iter = + channels_.find(GET_CHANNEL_ID(port_id)); if (iter == channels_.end()) return; - MessageChannel& channel = iter->second; - channel.extension_port->Send(new ViewMsg_HandleExtensionMessage( - channel.extension_port->routing_id(), message, channel_id)); + + // Figure out which port the ID corresponds to. + ResourceMessageFilter* dest = NULL; + if (IS_PORT1_ID(port_id)) { + dest = channel.port1; + DCHECK(source == channel.port2); + } else { + dest = channel.port2; + DCHECK(source == channel.port1); + } + + int source_port_id = GET_OPPOSITE_PORT_ID(port_id); + dest->Send(new ViewMsg_ExtensionHandleMessage(message, source_port_id)); +} + +void ExtensionMessageService::RendererReady(ResourceMessageFilter* renderer) { + AutoLock lock(renderers_lock_); + DCHECK(renderers_.find(renderer->GetProcessId()) == renderers_.end()); + renderers_[renderer->GetProcessId()] = renderer; } void ExtensionMessageService::RendererShutdown( - ResourceMessageFilter* renderer_port) { - DCHECK(MessageLoop::current() == - ChromeThread::GetMessageLoop(ChromeThread::IO)); + ResourceMessageFilter* renderer) { + { + AutoLock lock(renderers_lock_); + DCHECK(renderers_.find(renderer->GetProcessId()) != renderers_.end()); + renderers_.erase(renderer->GetProcessId()); + } // Close any channels that share this filter. // TODO(mpcomplete): should we notify the other side of the port? for (MessageChannelMap::iterator it = channels_.begin(); it != channels_.end(); ) { MessageChannelMap::iterator current = it++; - if (current->second.renderer_port == renderer_port) + if (current->second.port1 == renderer || current->second.port2 == renderer) channels_.erase(current); } } diff --git a/chrome/browser/extensions/extension_message_service.h b/chrome/browser/extensions/extension_message_service.h index 1b4040d..0020a30 100755 --- a/chrome/browser/extensions/extension_message_service.h +++ b/chrome/browser/extensions/extension_message_service.h @@ -8,13 +8,14 @@ #include <map> #include <string> +#include "base/lock.h" + class ExtensionView; class ResourceMessageFilter; -// This class manages message passing to and from extension processes. It -// maintains a list of available extensions, as well as a set of open channels. -// It should only be accessed on the IO thread, with the exception of -// RegisterExtensionView(). +// This class manages message passing between renderer processes. It maintains +// a list of available extensions and which renderers each lives in, as well as +// a set of open channels. // // Terminology: // channel: connection between two ports (one of which belongs to an extension) @@ -27,39 +28,53 @@ class ExtensionMessageService { ExtensionMessageService(); - // Registers an extension so that it can be referenced by its ID. This method - // should only be used by the UI thread. - void RegisterExtensionView(ExtensionView* view); + // --- UI thread only: + + // Register an extension and its corresponding renderer process. + void RegisterExtension(const std::string& extension_id, + int render_process_id); + + // --- IO thread only: // Given an extension's ID, opens a channel between the given renderer "port" // and that extension. Returns a channel ID to be used for posting messages // between the processes, or -1 if the extension doesn't exist. int OpenChannelToExtension(const std::string& extension_id, - ResourceMessageFilter* renderer_port); + ResourceMessageFilter* source); + + // Sends a message from a renderer to the given port. + void PostMessageFromRenderer(int port_id, const std::string& message, + ResourceMessageFilter* source); - // Sends a message to the extension via the given channel. - void PostMessageToExtension(int channel_id, const std::string& message); + // --- UI or IO thread: + + // Called to let us know that a renderer has been started. + void RendererReady(ResourceMessageFilter* port); // Called to let us know that a renderer is going away. - void RendererShutdown(ResourceMessageFilter* renderer_port); + void RendererShutdown(ResourceMessageFilter* port); + private: - class ExtensionFilter; - friend class ExtensionFilter; + // A map of extension ID to the render_process_id that the extension lives in. + typedef std::map<std::string, int> ProcessIDMap; + ProcessIDMap process_ids_; + + // A map of render_process_id to its corresponding message filter, which we + // use for sending messages. + typedef std::map<int, ResourceMessageFilter*> RendererMap; + RendererMap renderers_; - // Called when our ExtensionFilter is ready/going away. - void OnExtensionRegistered(ExtensionFilter* extension); - void OnExtensionUnregistered(ExtensionFilter* extension); + // Protects the two maps above, since each can be accessed on the IO thread + // or UI thread. Be careful not to hold this lock when calling external + // code (especially sending messages) to avoid deadlock. + Lock renderers_lock_; - // A map of extension ID to the extension port to communicate through. - // TODO(mpcomplete): Handle the case where there's multiple ExtensionViews - // in a given extension. - typedef std::map<std::string, ExtensionFilter*> ExtensionMap; - ExtensionMap extensions_; + // --- IO thread only: - // The connection between the renderer and extension. + // The connection between two renderers. struct MessageChannel { - ExtensionFilter* extension_port; - ResourceMessageFilter* renderer_port; + ResourceMessageFilter* port1; + ResourceMessageFilter* port2; }; // A map of channel ID to its channel object. @@ -67,7 +82,7 @@ class ExtensionMessageService { MessageChannelMap channels_; // For generating unique channel IDs. - int next_channel_id_; + int next_port_id_; }; #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_MESSAGE_SERVICE_H_ diff --git a/chrome/browser/extensions/extension_view.cc b/chrome/browser/extensions/extension_view.cc index 8213227..a9b3ed0 100755 --- a/chrome/browser/extensions/extension_view.cc +++ b/chrome/browser/extensions/extension_view.cc @@ -7,6 +7,7 @@ #include "chrome/browser/extensions/extension.h" #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/common/resource_bundle.h" #include "grit/browser_resources.h" @@ -26,8 +27,9 @@ void ExtensionView::CreatingRenderer() { render_view_host()->AllowExtensionBindings(); } -void ExtensionView::RenderViewCreated(RenderViewHost* render_view_host) { - ExtensionMessageService::GetInstance()->RegisterExtensionView(this); +void ExtensionView::RenderViewCreated(RenderViewHost* rvh) { + ExtensionMessageService::GetInstance()->RegisterExtension( + extension_->id(), render_view_host()->process()->pid()); } WebPreferences ExtensionView::GetWebkitPrefs() { |