summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions
diff options
context:
space:
mode:
authormpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-02 23:56:11 +0000
committermpcomplete@google.com <mpcomplete@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-02 23:56:11 +0000
commit75e5a879a500897b2409c49d4ef205e2e954c9fd (patch)
tree166ca53e8cc29520402efd7cc6ee2804edf744eb /chrome/browser/extensions
parent720ac73783ea732c130ff6d11dffa41919a25809 (diff)
downloadchromium_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-xchrome/browser/extensions/extension_message_service.cc154
-rwxr-xr-xchrome/browser/extensions/extension_message_service.h65
-rwxr-xr-xchrome/browser/extensions/extension_view.cc6
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() {