diff options
author | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-20 21:05:37 +0000 |
---|---|---|
committer | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-20 21:05:37 +0000 |
commit | 7120f1327bcd3f0c33a983ee4a61c277747bb566 (patch) | |
tree | da00ed514530587dd14f76941063d34a4afcda88 /chrome/browser/extensions/extension_message_service.cc | |
parent | 6d33519bd4d811112cef3d64d9c346531d65ab9b (diff) | |
download | chromium_src-7120f1327bcd3f0c33a983ee4a61c277747bb566.zip chromium_src-7120f1327bcd3f0c33a983ee4a61c277747bb566.tar.gz chromium_src-7120f1327bcd3f0c33a983ee4a61c277747bb566.tar.bz2 |
Changed the extension.connect() API not to broadcast to all tabs. Added a
more specific tabs.connect(tabId) API to connect to a specific tab.
Also changed the ExtensionMessageService from a singleton to a Profile-owned object.
BUG=12461
TEST=no
Review URL: http://codereview.chromium.org/155707
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21102 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions/extension_message_service.cc')
-rw-r--r-- | chrome/browser/extensions/extension_message_service.cc | 278 |
1 files changed, 151 insertions, 127 deletions
diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc index 70bfda7..5424ad2 100644 --- a/chrome/browser/extensions/extension_message_service.cc +++ b/chrome/browser/extensions/extension_message_service.cc @@ -10,7 +10,9 @@ #include "base/values.h" #include "chrome/browser/child_process_security_policy.h" #include "chrome/browser/chrome_thread.h" +#include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_tabs_module.h" +#include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/resource_message_filter.h" @@ -32,17 +34,25 @@ // 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) -namespace { -typedef std::map<URLRequestContext*, ExtensionMessageService*> InstanceMap; -struct SingletonData { - ~SingletonData() { - STLDeleteContainerPairSecondPointers(map.begin(), map.end()); - } - Lock lock; - InstanceMap map; +struct ExtensionMessageService::MessagePort { + IPC::Message::Sender* sender; + int routing_id; + + MessagePort(IPC::Message::Sender* sender = NULL, + int routing_id = MSG_ROUTING_CONTROL) : + sender(sender), routing_id(routing_id) {} +}; + +struct ExtensionMessageService::MessageChannel { + ExtensionMessageService::MessagePort opener; + ExtensionMessageService::MessagePort receiver; }; -static void DispatchOnConnect(IPC::Message::Sender* channel, int dest_port_id, + +namespace { + +static void DispatchOnConnect(const ExtensionMessageService::MessagePort& port, + int dest_port_id, const std::string& channel_name, const std::string& tab_json, const std::string& extension_id) { @@ -51,51 +61,39 @@ static void DispatchOnConnect(IPC::Message::Sender* channel, int dest_port_id, args.Set(1, Value::CreateStringValue(channel_name)); args.Set(2, Value::CreateStringValue(tab_json)); args.Set(3, Value::CreateStringValue(extension_id)); - channel->Send(new ViewMsg_ExtensionMessageInvoke( - ExtensionMessageService::kDispatchOnConnect, args)); + port.sender->Send(new ViewMsg_ExtensionMessageInvoke( + port.routing_id, ExtensionMessageService::kDispatchOnConnect, args)); } -static void DispatchOnDisconnect(IPC::Message::Sender* channel, - int source_port_id) { +static void DispatchOnDisconnect( + const ExtensionMessageService::MessagePort& port, int source_port_id) { ListValue args; args.Set(0, Value::CreateIntegerValue(source_port_id)); - channel->Send(new ViewMsg_ExtensionMessageInvoke( - ExtensionMessageService::kDispatchOnDisconnect, args)); + port.sender->Send(new ViewMsg_ExtensionMessageInvoke( + port.routing_id, ExtensionMessageService::kDispatchOnDisconnect, args)); } -static void DispatchOnMessage(IPC::Message::Sender* channel, +static void DispatchOnMessage(const ExtensionMessageService::MessagePort& port, const std::string& message, int source_port_id) { ListValue args; args.Set(0, Value::CreateStringValue(message)); args.Set(1, Value::CreateIntegerValue(source_port_id)); - channel->Send(new ViewMsg_ExtensionMessageInvoke( - ExtensionMessageService::kDispatchOnMessage, args)); + port.sender->Send(new ViewMsg_ExtensionMessageInvoke( + port.routing_id, ExtensionMessageService::kDispatchOnMessage, args)); } -static void DispatchEvent(IPC::Message::Sender* channel, +static void DispatchEvent(const ExtensionMessageService::MessagePort& port, const std::string& event_name, const std::string& event_args) { ListValue args; args.Set(0, Value::CreateStringValue(event_name)); args.Set(1, Value::CreateStringValue(event_args)); - channel->Send(new ViewMsg_ExtensionMessageInvoke( - ExtensionMessageService::kDispatchEvent, args)); -} - -static std::string GetChannelConnectEvent(const std::string& extension_id) { - return StringPrintf("channel-connect:%s", extension_id.c_str()); + port.sender->Send(new ViewMsg_ExtensionMessageInvoke( + port.routing_id, ExtensionMessageService::kDispatchEvent, args)); } } // namespace -// Since ExtensionMessageService is a collection of Singletons, we don't need to -// grab a reference to it when creating Tasks involving it. -template <> struct RunnableMethodTraits<ExtensionMessageService> { - static void RetainCallee(ExtensionMessageService*) {} - static void ReleaseCallee(ExtensionMessageService*) {} -}; - - const char ExtensionMessageService::kDispatchOnConnect[] = "Port.dispatchOnConnect"; const char ExtensionMessageService::kDispatchOnDisconnect[] = @@ -105,37 +103,29 @@ const char ExtensionMessageService::kDispatchOnMessage[] = const char ExtensionMessageService::kDispatchEvent[] = "Event.dispatchJSON"; -// static -ExtensionMessageService* ExtensionMessageService::GetInstance( - URLRequestContext* context) { - SingletonData* data = Singleton<SingletonData>::get(); - AutoLock lock(data->lock); +ExtensionMessageService::ExtensionMessageService(Profile* profile) + : ui_loop_(MessageLoop::current()), profile_(profile), next_port_id_(0) { + DCHECK_EQ(ui_loop_->type(), MessageLoop::TYPE_UI); - ExtensionMessageService* instance = data->map[context]; - if (!instance) { - instance = new ExtensionMessageService(); - data->map[context] = instance; - } - return instance; + registrar_.Add(this, NotificationType::RENDERER_PROCESS_TERMINATED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::RENDERER_PROCESS_CLOSED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_DELETED, + NotificationService::AllSources()); } -ExtensionMessageService::ExtensionMessageService() - : ui_loop_(NULL), initialized_(false), next_port_id_(0) { +ExtensionMessageService::~ExtensionMessageService() { } -void ExtensionMessageService::Init() { - DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); +void ExtensionMessageService::ProfileDestroyed() { + DCHECK_EQ(ui_loop_->type(), MessageLoop::TYPE_UI); - if (initialized_) - return; - initialized_ = true; + profile_ = NULL; - ui_loop_ = MessageLoop::current(); - - registrar_.Add(this, NotificationType::RENDERER_PROCESS_TERMINATED, - NotificationService::AllSources()); - registrar_.Add(this, NotificationType::RENDERER_PROCESS_CLOSED, - NotificationService::AllSources()); + // We remove notifications here because our destructor might be called on + // a non-UI thread. + registrar_.RemoveAll(); } void ExtensionMessageService::AddEventListener(std::string event_name, @@ -177,7 +167,6 @@ int ExtensionMessageService::OpenChannelToExtension( const std::string& channel_name, ResourceMessageFilter* source) { DCHECK_EQ(MessageLoop::current(), ChromeThread::GetMessageLoop(ChromeThread::IO)); - DCHECK(initialized_); // Create a channel ID for both sides of the channel. int port1_id = -1; @@ -187,51 +176,83 @@ int ExtensionMessageService::OpenChannelToExtension( // Each side of the port is given his own port ID. When they send messages, // we convert to the opposite port ID. See PostMessageFromRenderer. ui_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &ExtensionMessageService::OpenChannelOnUIThread, - routing_id, port2_id, source->GetProcessId(), extension_id, + NewRunnableMethod(this, + &ExtensionMessageService::OpenChannelToExtensionOnUIThread, + source->GetProcessId(), routing_id, port2_id, extension_id, + channel_name)); + + return port1_id; +} + +int ExtensionMessageService::OpenChannelToTab( + int routing_id, int tab_id, const std::string& extension_id, + const std::string& channel_name, ResourceMessageFilter* source) { + DCHECK_EQ(MessageLoop::current(), + ChromeThread::GetMessageLoop(ChromeThread::IO)); + + // Create a channel ID for both sides of the channel. + int port1_id = -1; + int port2_id = -1; + AllocatePortIdPair(&port1_id, &port2_id); + + // Each side of the port is given his own port ID. When they send messages, + // we convert to the opposite port ID. See PostMessageFromRenderer. + ui_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, + &ExtensionMessageService::OpenChannelToTabOnUIThread, + source->GetProcessId(), routing_id, port2_id, tab_id, extension_id, channel_name)); return port1_id; } -void ExtensionMessageService::OpenChannelOnUIThread( - int source_routing_id, int receivers_port_id, int source_process_id, +void ExtensionMessageService::OpenChannelToExtensionOnUIThread( + int source_process_id, int source_routing_id, int receiver_port_id, const std::string& extension_id, const std::string& channel_name) { + if (!profile_) + return; + RenderProcessHost* source = RenderProcessHost::FromID(source_process_id); - OpenChannelOnUIThreadImpl(source_routing_id, receivers_port_id, - source_process_id, source, extension_id, + MessagePort receiver( + profile_->GetExtensionProcessManager()->GetExtensionProcess(extension_id), + MSG_ROUTING_CONTROL); + OpenChannelOnUIThreadImpl(source, source_process_id, source_routing_id, + receiver, receiver_port_id, extension_id, channel_name); } -void ExtensionMessageService::OpenChannelOnUIThreadImpl( - int source_routing_id, int receivers_port_id, int source_process_id, - IPC::Message::Sender* source, const std::string& extension_id, +void ExtensionMessageService::OpenChannelToTabOnUIThread( + int source_process_id, int source_routing_id, int receiver_port_id, + int tab_id, const std::string& extension_id, const std::string& channel_name) { + RenderProcessHost* source = RenderProcessHost::FromID(source_process_id); + TabContents* contents; + MessagePort receiver; + if (ExtensionTabUtil::GetTabById(tab_id, source->profile(), + NULL, NULL, &contents, NULL)) { + receiver.sender = contents->render_view_host(); + receiver.routing_id = contents->render_view_host()->routing_id(); + } + OpenChannelOnUIThreadImpl(source, source_process_id, source_routing_id, + receiver, receiver_port_id, extension_id, + channel_name); +} + +void ExtensionMessageService::OpenChannelOnUIThreadImpl( + IPC::Message::Sender* source, int source_process_id, int source_routing_id, + const MessagePort& receiver, int receiver_port_id, + const std::string& extension_id, const std::string& channel_name) { DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); - if (!source) - return; // Source closed while task was in flight. + // TODO(mpcomplete): notify source if reciever doesn't exist + if (!source || !receiver.sender) + return; // Closed while in flight. linked_ptr<MessageChannel> channel(new MessageChannel); - channel->opener.insert(source); - - // Get the list of processes that are listening for this extension's channel - // connect event. - std::string event_name = GetChannelConnectEvent(extension_id); - std::set<int>& pids = listeners_[event_name]; - for (std::set<int>::iterator pid = pids.begin(); pid != pids.end(); ++pid) { - RenderProcessHost* renderer = RenderProcessHost::FromID(*pid); - if (!renderer) - continue; - channel->receivers.insert(renderer); - } - if (channel->receivers.empty()) { - // Either no one is listening, or all listeners have since closed. - // TODO(mpcomplete): should we notify the source? - return; - } + channel->opener = MessagePort(source, MSG_ROUTING_CONTROL); + channel->receiver = receiver; - channels_[GET_CHANNEL_ID(receivers_port_id)] = channel; + channels_[GET_CHANNEL_ID(receiver_port_id)] = channel; // Include info about the opener's tab (if it was a tab). std::string tab_json = "null"; @@ -242,20 +263,17 @@ void ExtensionMessageService::OpenChannelOnUIThreadImpl( JSONWriter::Write(tab_value, false, &tab_json); } - // Broadcast the connect event to the receivers. Give them the opener's - // port ID (the opener has the opposite port ID). - for (MessageChannel::Ports::iterator it = channel->receivers.begin(); - it != channel->receivers.end(); ++it) { - DispatchOnConnect(*it, receivers_port_id, channel_name, tab_json, - extension_id); - } + // Send the connect event to the receiver. Give it the opener's port ID (the + // opener has the opposite port ID). + DispatchOnConnect(receiver, receiver_port_id, channel_name, tab_json, + extension_id); } int ExtensionMessageService::OpenAutomationChannelToExtension( int source_process_id, int routing_id, const std::string& extension_id, IPC::Message::Sender* source) { DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); - DCHECK(initialized_); + DCHECK(profile_); int port1_id = -1; int port2_id = -1; @@ -267,8 +285,11 @@ int ExtensionMessageService::OpenAutomationChannelToExtension( // This isn't really appropriate here, the originating tab // information should be supplied by the caller for // automation-initiated ports. - OpenChannelOnUIThreadImpl(routing_id, port2_id, source_process_id, - source, extension_id, ""); + MessagePort receiver( + profile_->GetExtensionProcessManager()->GetExtensionProcess(extension_id), + MSG_ROUTING_CONTROL); + OpenChannelOnUIThreadImpl(source, source_process_id, routing_id, receiver, + port2_id, extension_id, ""); return port1_id; } @@ -287,15 +308,10 @@ void ExtensionMessageService::CloseChannelImpl( DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); // Notify the other side. - MessageChannel::Ports* ports = - IS_OPENER_PORT_ID(closing_port_id) ? - &channel_iter->second->receivers : &channel_iter->second->opener; - - for (MessageChannel::Ports::iterator it = ports->begin(); - it != ports->end(); ++it) { - DispatchOnDisconnect(*it, GET_OPPOSITE_PORT_ID(closing_port_id)); - } + const MessagePort& port = IS_OPENER_PORT_ID(closing_port_id) ? + channel_iter->second->receiver : channel_iter->second->opener; + DispatchOnDisconnect(port, GET_OPPOSITE_PORT_ID(closing_port_id)); channels_.erase(channel_iter); } @@ -310,14 +326,10 @@ void ExtensionMessageService::PostMessageFromRenderer( // Figure out which port the ID corresponds to. int dest_port_id = GET_OPPOSITE_PORT_ID(source_port_id); - MessageChannel::Ports* ports = - IS_OPENER_PORT_ID(dest_port_id) ? - &iter->second->opener : &iter->second->receivers; + const MessagePort& port = IS_OPENER_PORT_ID(dest_port_id) ? + iter->second->opener : iter->second->receiver; - for (MessageChannel::Ports::iterator it = ports->begin(); - it != ports->end(); ++it) { - DispatchOnMessage(*it, message, dest_port_id); - } + DispatchOnMessage(port, message, dest_port_id); } void ExtensionMessageService::DispatchEventToRenderers( @@ -346,29 +358,41 @@ void ExtensionMessageService::Observe(NotificationType type, const NotificationDetails& details) { DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); - DCHECK(type.value == NotificationType::RENDERER_PROCESS_TERMINATED || - type.value == NotificationType::RENDERER_PROCESS_CLOSED); - - RenderProcessHost* renderer = Source<RenderProcessHost>(source).ptr(); + switch (type.value) { + case NotificationType::RENDERER_PROCESS_TERMINATED: + case NotificationType::RENDERER_PROCESS_CLOSED: { + RenderProcessHost* renderer = Source<RenderProcessHost>(source).ptr(); + OnSenderClosed(renderer); + + // Remove this renderer from our listener maps. + for (ListenerMap::iterator it = listeners_.begin(); + it != listeners_.end(); ) { + ListenerMap::iterator current = it++; + current->second.erase(renderer->pid()); + if (current->second.empty()) + listeners_.erase(current); + } + break; + } + case NotificationType::RENDER_VIEW_HOST_DELETED: + OnSenderClosed(Details<RenderViewHost>(details).ptr()); + break; + default: + NOTREACHED(); + return; + } +} +void ExtensionMessageService::OnSenderClosed(IPC::Message::Sender* sender) { // Close any channels that share this renderer. We notify the opposite // port that his pair has closed. for (MessageChannelMap::iterator it = channels_.begin(); it != channels_.end(); ) { MessageChannelMap::iterator current = it++; - if (current->second->opener.count(renderer) > 0) { + if (current->second->opener.sender == sender) { CloseChannelImpl(current, GET_CHANNEL_OPENER_ID(current->first)); - } else if (current->second->receivers.count(renderer) > 0) { + } else if (current->second->receiver.sender == sender) { CloseChannelImpl(current, GET_CHANNEL_RECEIVERS_ID(current->first)); } } - - // Remove this renderer from our listener maps. - for (ListenerMap::iterator it = listeners_.begin(); - it != listeners_.end(); ) { - ListenerMap::iterator current = it++; - current->second.erase(renderer->pid()); - if (current->second.empty()) - listeners_.erase(current); - } } |