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 | |
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
32 files changed, 448 insertions, 241 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index ed4fd46..1ce2c87 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -2730,8 +2730,10 @@ bool AutomationProvider::InterceptBrowserEventMessageFromExternalHost( return false; } - ExtensionMessageService::GetInstance(profile()->GetRequestContext())-> - DispatchEventToRenderers(event_name.c_str(), json_args); + if (profile()->GetExtensionMessageService()) { + profile()->GetExtensionMessageService()-> + DispatchEventToRenderers(event_name.c_str(), json_args); + } return true; } diff --git a/chrome/browser/automation/extension_port_container.cc b/chrome/browser/automation/extension_port_container.cc index 581da75..e814fcc 100644 --- a/chrome/browser/automation/extension_port_container.cc +++ b/chrome/browser/automation/extension_port_container.cc @@ -25,8 +25,7 @@ ExtensionPortContainer::ExtensionPortContainer(AutomationProvider* automation, int tab_handle) : automation_(automation), service_(NULL), port_id_(-1), tab_handle_(tab_handle) { - URLRequestContext* context = automation_->profile()->GetRequestContext(); - service_ = ExtensionMessageService::GetInstance(context); + service_ = automation_->profile()->GetExtensionMessageService();; DCHECK(service_); } diff --git a/chrome/browser/automation/extension_port_container.h b/chrome/browser/automation/extension_port_container.h index d9244b0..8eedd09 100644 --- a/chrome/browser/automation/extension_port_container.h +++ b/chrome/browser/automation/extension_port_container.h @@ -8,6 +8,7 @@ #include <string> #include "base/basictypes.h" +#include "base/ref_counted.h" #include "chrome/common/ipc_message.h" class AutomationProvider; @@ -66,7 +67,7 @@ class ExtensionPortContainer : public IPC::Message::Sender { AutomationProvider* automation_; // The extension message service. - ExtensionMessageService* service_; + scoped_refptr<ExtensionMessageService> service_; // Our assigned port id. int port_id_; diff --git a/chrome/browser/extensions/extension_bookmarks_module.cc b/chrome/browser/extensions/extension_bookmarks_module.cc index 9f978ef..2d7cbb3 100644 --- a/chrome/browser/extensions/extension_bookmarks_module.cc +++ b/chrome/browser/extensions/extension_bookmarks_module.cc @@ -154,8 +154,10 @@ void ExtensionBookmarkEventRouter::Observe(BookmarkModel* model) { void ExtensionBookmarkEventRouter::DispatchEvent(Profile *profile, const char* event_name, const std::string json_args) { - ExtensionMessageService::GetInstance(profile->GetRequestContext())-> - DispatchEventToRenderers(event_name, json_args); + if (profile->GetExtensionMessageService()) { + profile->GetExtensionMessageService()-> + DispatchEventToRenderers(event_name, json_args); + } } void ExtensionBookmarkEventRouter::Loaded(BookmarkModel* model) { diff --git a/chrome/browser/extensions/extension_browser_event_router.cc b/chrome/browser/extensions/extension_browser_event_router.cc index a689d32..1fcb3e7 100644 --- a/chrome/browser/extensions/extension_browser_event_router.cc +++ b/chrome/browser/extensions/extension_browser_event_router.cc @@ -85,8 +85,10 @@ ExtensionBrowserEventRouter* ExtensionBrowserEventRouter::GetInstance() { static void DispatchEvent(Profile* profile, const char* event_name, const std::string json_args) { - ExtensionMessageService::GetInstance(profile->GetRequestContext())-> - DispatchEventToRenderers(event_name, json_args); + if (profile->GetExtensionMessageService()) { + profile->GetExtensionMessageService()-> + DispatchEventToRenderers(event_name, json_args); + } } static void DispatchSimpleBrowserEvent(Profile* profile, diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index 4b13d98..57a1212 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -189,13 +189,10 @@ ExtensionFunctionDispatcher::ExtensionFunctionDispatcher( ALLOW_THIS_IN_INITIALIZER_LIST(peer_(new Peer(this))) { all_instances()->insert(this); - // Ensure the message service is initialized. - ExtensionMessageService::GetInstance(profile()->GetRequestContext())->Init(); - // Notify the ExtensionProcessManager that the view was created. ExtensionProcessManager* epm = profile()->GetExtensionProcessManager(); epm->RegisterExtensionProcess(extension_id(), - render_view_host->process()->pid()); + render_view_host->process()->pid()); } ExtensionFunctionDispatcher::~ExtensionFunctionDispatcher() { 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); - } } diff --git a/chrome/browser/extensions/extension_message_service.h b/chrome/browser/extensions/extension_message_service.h index 77970bc..832451d 100644 --- a/chrome/browser/extensions/extension_message_service.h +++ b/chrome/browser/extensions/extension_message_service.h @@ -11,36 +11,40 @@ #include "base/linked_ptr.h" #include "base/lock.h" +#include "base/ref_counted.h" #include "chrome/common/ipc_message.h" #include "chrome/common/notification_registrar.h" class MessageLoop; +class Profile; class RenderProcessHost; class ResourceMessageFilter; class URLRequestContext; // This class manages message and event passing between renderer processes. -// It maintains a list of processes that are listening to events (including -// messaging events), as well as a set of open channels. -// +// It maintains a list of processes that are listening to events and a set of +// open channels. +// // Messaging works this way: // - An extension-owned script context (like a toolstrip or a content script) -// adds an event listener to the "onConnect" event. We keep track here of a -// list of "listeners" that registered interest in receiving extension -// messages. -// - Another context calls "connect()" to open a channel to every listener -// owned by the same extension. This is a broadcast event, so every listener -// will get notified. +// adds an event listener to the "onConnect" event. +// - Another context calls "extension.connect()" to open a channel to the +// extension process, or an extension context calls "tabs.connect(tabId)" to +// open a channel to the content scripts for the given tab. The EMS notifies +// the target process/tab, which then calls the onConnect event in every +// context owned by the connecting extension in that process/tab. // - Once the channel is established, either side can call postMessage to send // a message to the opposite side of the channel, which may have multiple // listeners. // // Terminology: -// channel: connection between two ports (one side of which can have multiple -// listeners) -// port: one or more IPC::Message::Sender interfaces through which we -// communicate to process(es). These are generally RenderProcessHosts. -class ExtensionMessageService : public NotificationObserver { +// channel: connection between two ports +// port: an IPC::Message::Sender interface and an optional routing_id (in the +// case that the port is a tab). The Sender is usually either a +// RenderProcessHost or a RenderViewHost. +class ExtensionMessageService : + public base::RefCountedThreadSafe<ExtensionMessageService>, + public NotificationObserver { public: // Javascript function name constants. static const char kDispatchOnConnect[]; @@ -49,16 +53,18 @@ class ExtensionMessageService : public NotificationObserver { static const char kDispatchEvent[]; static const char kDispatchError[]; - // Returns the message service for the given context. Messages can only - // be sent within a single context. - static ExtensionMessageService* GetInstance(URLRequestContext* context); - - ExtensionMessageService(); + // A messaging channel. Note that the opening port can be the same as the + // receiver, if an extension toolstrip wants to talk to its tab (for example). + struct MessageChannel; + struct MessagePort; // --- UI thread only: - // UI-thread specific initialization. Does nothing if called more than once. - void Init(); + ExtensionMessageService(Profile* profile); + ~ExtensionMessageService(); + + // Notification that our owning profile is going away. + void ProfileDestroyed(); // Add or remove |render_process_pid| as a listener for |event_name|. void AddEventListener(std::string event_name, int render_process_id); @@ -83,11 +89,6 @@ class ExtensionMessageService : public NotificationObserver { const std::string& extension_id, IPC::Message::Sender* source); - // NotificationObserver interface. - void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - // --- IO thread only: // Given an extension's ID, opens a channel between the given renderer "port" @@ -99,18 +100,16 @@ class ExtensionMessageService : public NotificationObserver { int OpenChannelToExtension(int routing_id, const std::string& extension_id, const std::string& channel_name, ResourceMessageFilter* source); - - private: - // A messaging channel. Since messages are broadcast, the channel can have - // multiple processes listening for messages. Note that the opening port - // can also be among the receivers, if an extension toolstrip wants to talk - // to its tab (for example). - struct MessageChannel { - typedef std::set<IPC::Message::Sender*> Ports; - Ports opener; // only 1 opener, but we use a set to simplify logic - Ports receivers; - }; + // Same as above, but opens a channel to the tab with the given ID. Messages + // are restricted to that tab, so if there are multiple tabs in that process, + // only the targeted tab will receive messages. + int OpenChannelToTab(int routing_id, int tab_id, + const std::string& extension_id, + const std::string& channel_name, + ResourceMessageFilter* source); + + private: // A map of channel ID to its channel object. typedef std::map<int, linked_ptr<MessageChannel> > MessageChannelMap; @@ -127,15 +126,30 @@ class ExtensionMessageService : public NotificationObserver { // Handles channel creation and notifies the destinations that a channel was // opened. - void OpenChannelOnUIThread(int source_routing_id, - int source_port_id, int source_process_id, - const std::string& extension_id, const std::string& channel_name); + void OpenChannelToExtensionOnUIThread( + int source_process_id, int source_routing_id, int receiver_port_id, + const std::string& extension_id, const std::string& channel_name); + + void 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); // Common between OpenChannelOnUIThread and OpenAutomationChannelToExtension. void OpenChannelOnUIThreadImpl( - int source_routing_id, int source_port_id, int source_process_id, - IPC::Message::Sender* source, const std::string& extension_id, - const std::string& channel_name); + 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); + + // NotificationObserver interface. + void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // An IPC sender that might be in our list of channels has closed. + void OnSenderClosed(IPC::Message::Sender* sender); + + Profile* profile_; NotificationRegistrar registrar_; @@ -148,9 +162,6 @@ class ExtensionMessageService : public NotificationObserver { // --- UI or IO thread: - // True if Init has been called. - bool initialized_; - // For generating unique channel IDs. int next_port_id_; diff --git a/chrome/browser/extensions/extension_messages_unittest.cc b/chrome/browser/extensions/extension_messages_unittest.cc index c2e4540..7da3f38 100644 --- a/chrome/browser/extensions/extension_messages_unittest.cc +++ b/chrome/browser/extensions/extension_messages_unittest.cc @@ -17,14 +17,14 @@ static void DispatchOnConnect(int source_port_id, const std::string& name, args.Set(2, Value::CreateStringValue(tab_json)); args.Set(3, Value::CreateStringValue("")); // extension ID is empty for tests RendererExtensionBindings::Invoke( - ExtensionMessageService::kDispatchOnConnect, args); + ExtensionMessageService::kDispatchOnConnect, args, NULL); } static void DispatchOnDisconnect(int source_port_id) { ListValue args; args.Set(0, Value::CreateIntegerValue(source_port_id)); RendererExtensionBindings::Invoke( - ExtensionMessageService::kDispatchOnDisconnect, args); + ExtensionMessageService::kDispatchOnDisconnect, args, NULL); } static void DispatchOnMessage(const std::string& message, int source_port_id) { @@ -32,7 +32,7 @@ static void DispatchOnMessage(const std::string& message, int source_port_id) { args.Set(0, Value::CreateStringValue(message)); args.Set(1, Value::CreateIntegerValue(source_port_id)); RendererExtensionBindings::Invoke( - ExtensionMessageService::kDispatchOnMessage, args); + ExtensionMessageService::kDispatchOnMessage, args, NULL); } // Tests that the bindings for opening a channel to an extension and sending diff --git a/chrome/browser/extensions/extension_process_manager.cc b/chrome/browser/extensions/extension_process_manager.cc index 3018ea8..57b969b 100644 --- a/chrome/browser/extensions/extension_process_manager.cc +++ b/chrome/browser/extensions/extension_process_manager.cc @@ -85,7 +85,7 @@ ExtensionHost* ExtensionProcessManager::CreateBackgroundHost( } void ExtensionProcessManager::RegisterExtensionProcess( - std::string extension_id, int process_id) { + const std::string& extension_id, int process_id) { ProcessIDMap::const_iterator it = process_ids_.find(extension_id); if (it != process_ids_.end() && (*it).second == process_id) return; @@ -117,6 +117,17 @@ void ExtensionProcessManager::UnregisterExtensionProcess(int process_id) { } } +RenderProcessHost* ExtensionProcessManager::GetExtensionProcess( + const std::string& extension_id) { + ProcessIDMap::const_iterator it = process_ids_.find(extension_id); + if (it == process_ids_.end()) + return NULL; + + RenderProcessHost* rph = RenderProcessHost::FromID(it->second); + DCHECK(rph) << "We should have unregistered this host."; + return rph; +} + SiteInstance* ExtensionProcessManager::GetSiteInstanceForURL(const GURL& url) { return browsing_instance_->GetSiteInstanceForURL(url); } diff --git a/chrome/browser/extensions/extension_process_manager.h b/chrome/browser/extensions/extension_process_manager.h index e3d6b76..d2af74d 100644 --- a/chrome/browser/extensions/extension_process_manager.h +++ b/chrome/browser/extensions/extension_process_manager.h @@ -21,6 +21,7 @@ class ExtensionView; #endif class GURL; class Profile; +class RenderProcessHost; class SiteInstance; // Manages dynamic state of running Chromium extensions. There is one instance @@ -45,13 +46,17 @@ class ExtensionProcessManager : public NotificationObserver { // Returns the SiteInstance that the given URL belongs to. SiteInstance* GetSiteInstanceForURL(const GURL& url); - // Register an extension process by |extension_id| and specifying which + // Registers an extension process by |extension_id| and specifying which // |process_id| it belongs to. - void RegisterExtensionProcess(std::string extension_id, int process_id); + void RegisterExtensionProcess(const std::string& extension_id, + int process_id); - // Unregister an extension process with specified |process_id|. + // Unregisters an extension process with specified |process_id|. void UnregisterExtensionProcess(int process_id); + // Returns the process that the extension with the given ID is running in. + RenderProcessHost* GetExtensionProcess(const std::string& extension_id); + // NotificationObserver: virtual void Observe(NotificationType type, const NotificationSource& source, diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index 36517c4..9017c5f 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -15,6 +15,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_theme_provider.h" #include "chrome/browser/download/download_manager.h" +#include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/extensions/user_script_master.h" @@ -220,6 +221,10 @@ class OffTheRecordProfileImpl : public Profile, return NULL; } + virtual ExtensionMessageService* GetExtensionMessageService() { + return NULL; + } + virtual SSLHostState* GetSSLHostState() { if (!ssl_host_state_.get()) ssl_host_state_.reset(new SSLHostState()); @@ -499,6 +504,7 @@ ProfileImpl::ProfileImpl(const FilePath& path) &ProfileImpl::EnsureSessionServiceCreated); extension_process_manager_.reset(new ExtensionProcessManager(this)); + extension_message_service_ = new ExtensionMessageService(this); PrefService* prefs = GetPrefs(); prefs->AddPrefObserver(prefs::kSpellCheckDictionary, this); @@ -663,6 +669,8 @@ ProfileImpl::~ProfileImpl() { history_service_ = NULL; bookmark_bar_model_.reset(); + extension_message_service_->ProfileDestroyed(); + MarkAsCleanShutdown(); } @@ -715,6 +723,10 @@ ExtensionProcessManager* ProfileImpl::GetExtensionProcessManager() { return extension_process_manager_.get(); } +ExtensionMessageService* ProfileImpl::GetExtensionMessageService() { + return extension_message_service_.get(); +} + SSLHostState* ProfileImpl::GetSSLHostState() { if (!ssl_host_state_.get()) ssl_host_state_.reset(new SSLHostState()); diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h index e16bfe1..fb72da5 100644 --- a/chrome/browser/profile.h +++ b/chrome/browser/profile.h @@ -31,6 +31,7 @@ class ChromeURLRequestContext; class DownloadManager; class Extension; class ExtensionProcessManager; +class ExtensionMessageService; class ExtensionsService; class HistoryService; class NavigationController; @@ -132,6 +133,10 @@ class Profile { // profile. The instance is created at startup. virtual ExtensionProcessManager* GetExtensionProcessManager() = 0; + // Retrieves a pointer to the ExtensionMessageService associated with this + // profile. The instance is created at startup. + virtual ExtensionMessageService* GetExtensionMessageService() = 0; + // Retrieves a pointer to the SSLHostState associated with this profile. // The SSLHostState is lazily created the first time that this method is // called. @@ -335,6 +340,7 @@ class ProfileImpl : public Profile, virtual net::ForceTLSState* GetForceTLSState(); virtual ExtensionsService* GetExtensionsService(); virtual ExtensionProcessManager* GetExtensionProcessManager(); + virtual ExtensionMessageService* GetExtensionMessageService(); virtual HistoryService* GetHistoryService(ServiceAccessType sat); virtual WebDataService* GetWebDataService(ServiceAccessType sat); virtual PasswordStore* GetPasswordStore(ServiceAccessType sat); @@ -413,6 +419,7 @@ class ProfileImpl : public Profile, scoped_refptr<ExtensionsService> extensions_service_; scoped_refptr<UserScriptMaster> user_script_master_; scoped_ptr<ExtensionProcessManager> extension_process_manager_; + scoped_refptr<ExtensionMessageService> extension_message_service_; scoped_ptr<SSLHostState> ssl_host_state_; scoped_ptr<net::ForceTLSState> force_tls_state_; scoped_ptr<PrefService> prefs_; diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index bad0e3e..f8142f5 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -963,17 +963,22 @@ void BrowserRenderProcessHost::Observe(NotificationType type, void BrowserRenderProcessHost::OnExtensionAddListener( const std::string& event_name) { - ExtensionMessageService::GetInstance(profile()->GetRequestContext())-> - AddEventListener(event_name, pid()); + if (profile()->GetExtensionMessageService()) { + profile()->GetExtensionMessageService()->AddEventListener(event_name, + pid()); + } } void BrowserRenderProcessHost::OnExtensionRemoveListener( const std::string& event_name) { - ExtensionMessageService::GetInstance(profile()->GetRequestContext())-> - RemoveEventListener(event_name, pid()); + if (profile()->GetExtensionMessageService()) { + profile()->GetExtensionMessageService()->RemoveEventListener(event_name, + pid()); + } } void BrowserRenderProcessHost::OnExtensionCloseChannel(int port_id) { - ExtensionMessageService::GetInstance(profile()->GetRequestContext())-> - CloseChannel(port_id); + if (profile()->GetExtensionMessageService()) { + profile()->GetExtensionMessageService()->CloseChannel(port_id); + } } diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc index c788560..0f4df04 100644 --- a/chrome/browser/renderer_host/render_view_host.cc +++ b/chrome/browser/renderer_host/render_view_host.cc @@ -1555,9 +1555,10 @@ void RenderViewHost::SendExtensionResponse(int request_id, bool success, void RenderViewHost::OnExtensionPostMessage( int port_id, const std::string& message) { - URLRequestContext* context = process()->profile()->GetRequestContext(); - ExtensionMessageService::GetInstance(context)-> - PostMessageFromRenderer(port_id, message); + if (process()->profile()->GetExtensionMessageService()) { + process()->profile()->GetExtensionMessageService()-> + PostMessageFromRenderer(port_id, message); + } } void RenderViewHost::OnAccessibilityFocusChange(int acc_obj_id) { diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h index 02b19fc..74fe24e 100644 --- a/chrome/browser/renderer_host/render_widget_host.h +++ b/chrome/browser/renderer_host/render_widget_host.h @@ -115,7 +115,8 @@ struct ViewHostMsg_ShowPopup_Params; // anything else. When the view is live, these messages are forwarded to it by // the RenderWidgetHost's IPC message map. // -class RenderWidgetHost : public IPC::Channel::Listener { +class RenderWidgetHost : public IPC::Channel::Listener, + public IPC::Channel::Sender { public: // An interface that gets called whenever a paint occurs. // Used in performance tests. diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index bf542cd..41b0b35 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -147,6 +147,7 @@ ResourceMessageFilter::ResourceMessageFilter( request_context_(profile->GetRequestContext()), media_request_context_(profile->GetRequestContextForMedia()), extensions_request_context_(profile->GetRequestContextForExtensions()), + extensions_message_service_(profile->GetExtensionMessageService()), profile_(profile), render_widget_helper_(render_widget_helper), audio_renderer_host_(audio_renderer_host), @@ -334,6 +335,8 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& message) { #endif IPC_MESSAGE_HANDLER(ViewHostMsg_OpenChannelToExtension, OnOpenChannelToExtension) + IPC_MESSAGE_HANDLER(ViewHostMsg_OpenChannelToTab, + OnOpenChannelToTab) IPC_MESSAGE_HANDLER(ViewHostMsg_CloseIdleConnections, OnCloseIdleConnections) IPC_MESSAGE_HANDLER(ViewHostMsg_SetCacheMode, @@ -901,8 +904,23 @@ void ResourceMessageFilter::OnFreeTransportDIB( void ResourceMessageFilter::OnOpenChannelToExtension( int routing_id, const std::string& extension_id, const std::string& channel_name, int* port_id) { - *port_id = ExtensionMessageService::GetInstance(request_context_.get())-> - OpenChannelToExtension(routing_id, extension_id, channel_name, this); + if (extensions_message_service_.get()) { + *port_id = extensions_message_service_-> + OpenChannelToExtension(routing_id, extension_id, channel_name, this); + } else { + *port_id = -1; + } +} + +void ResourceMessageFilter::OnOpenChannelToTab( + int routing_id, int tab_id, const std::string& extension_id, + const std::string& channel_name, int* port_id) { + if (extensions_message_service_.get()) { + *port_id = extensions_message_service_-> + OpenChannelToTab(routing_id, tab_id, extension_id, channel_name, this); + } else { + *port_id = -1; + } } bool ResourceMessageFilter::CheckBenchmarkingEnabled() { diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index 613dd77..7ac5b85 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -33,6 +33,7 @@ class AppCacheDispatcherHost; class AudioRendererHost; class Clipboard; class DOMStorageDispatcherHost; +class ExtensionMessageService; class Profile; class RenderWidgetHelper; class SpellChecker; @@ -220,6 +221,9 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, void OnOpenChannelToExtension(int routing_id, const std::string& extension_id, const std::string& channel_name, int* port_id); + void OnOpenChannelToTab(int routing_id, int tab_id, + const std::string& extension_id, + const std::string& channel_name, int* port_id); void OnCloseIdleConnections(); void OnSetCacheMode(bool enabled); @@ -279,8 +283,12 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, // A request context specific for media resources. scoped_refptr<URLRequestContext> media_request_context_; + // A request context that holds a cookie store for chrome-extension URLs. scoped_refptr<URLRequestContext> extensions_request_context_; + // Used for routing extension messages. + scoped_refptr<ExtensionMessageService> extensions_message_service_; + // A pointer to the profile associated with this filter. // // DANGER! Do not dereference this pointer! This class lives on the I/O thread diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 71675fe..301bf34 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -552,11 +552,14 @@ IPC_BEGIN_MESSAGES(View) std::string /* response */, std::string /* error */) - // Call a javascript function in every registered context in this process. + // This message is optionally routed. If used as a control message, it + // will call a javascript function in every registered context in the + // target process. If routed, it will be restricted to the contexts that + // are part of the target RenderView. // |args| is a list of primitive Value types that are passed to the function. - IPC_MESSAGE_CONTROL2(ViewMsg_ExtensionMessageInvoke, - std::string /* function_name */, - ListValue /* args */) + IPC_MESSAGE_ROUTED2(ViewMsg_ExtensionMessageInvoke, + std::string /* function_name */, + ListValue /* args */) // Tell the renderer process all known extension function names. IPC_MESSAGE_CONTROL1(ViewMsg_Extension_SetFunctionNames, @@ -1428,11 +1431,13 @@ IPC_BEGIN_MESSAGES(ViewHost) std::string /* channel_name */, int /* port_id */) - // Get a port handle to the given tab's process. The handle can be used for - // sending messages to the extension. - IPC_SYNC_MESSAGE_CONTROL2_1(ViewHostMsg_OpenChannelToTab, + // Get a port handle to the given tab. The handle can be used for sending + // messages to the extension. + IPC_SYNC_MESSAGE_CONTROL4_1(ViewHostMsg_OpenChannelToTab, int /* routing_id */, int /* tab_id */, + std::string /* extension_id */, + std::string /* channel_name */, int /* port_id */) // Send a message to an extension process. The handle is the value returned diff --git a/chrome/renderer/extensions/bindings_utils.h b/chrome/renderer/extensions/bindings_utils.h index 2c81b0c..324a73c 100644 --- a/chrome/renderer/extensions/bindings_utils.h +++ b/chrome/renderer/extensions/bindings_utils.h @@ -65,11 +65,16 @@ struct ContextInfo { // was injected in. This is empty if the context is not a content script. v8::Persistent<v8::Context> parent_context; + // The RenderView that this context belongs to. This is not guaranteed to be + // a valid pointer, and is used for comparisons only. Do not dereference. + RenderView* render_view; + ContextInfo(v8::Persistent<v8::Context> context, const std::string& extension_id, - v8::Persistent<v8::Context> parent_context) + v8::Persistent<v8::Context> parent_context, + RenderView* render_view) : context(context), extension_id(extension_id), - parent_context(parent_context) {} + parent_context(parent_context), render_view(render_view) {} }; typedef std::list< linked_ptr<ContextInfo> > ContextList; diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc index bb8df28..9d7104c 100644 --- a/chrome/renderer/extensions/event_bindings.cc +++ b/chrome/renderer/extensions/event_bindings.cc @@ -14,6 +14,8 @@ #include "chrome/renderer/render_thread.h" #include "chrome/renderer/render_view.h" #include "grit/renderer_resources.h" +#include "webkit/api/public/WebDataSource.h" +#include "webkit/api/public/WebURLRequest.h" #include "webkit/glue/webframe.h" using bindings_utils::CallFunctionInContext; @@ -29,6 +31,7 @@ namespace { // Keep a local cache of RenderThread so that we can mock it out for unit tests. static RenderThreadBase* render_thread = NULL; +static bool in_unit_tests = false; // Set to true if these bindings are registered. Will be false when extensions // are disabled. @@ -111,6 +114,7 @@ v8::Extension* EventBindings::Get() { // static void EventBindings::SetRenderThread(RenderThreadBase* thread) { render_thread = thread; + in_unit_tests = true; } // static @@ -150,12 +154,17 @@ static void HandleContextDestroyed(ContextList::iterator context_iter, (*context_iter)->context.ClearWeak(); (*context_iter)->context.Dispose(); (*context_iter)->context.Clear(); + + if (!(*context_iter)->parent_context.IsEmpty()) { + (*context_iter)->parent_context.Dispose(); + (*context_iter)->parent_context.Clear(); + } + GetContexts().erase(context_iter); } static void ContextWeakReferenceCallback(v8::Persistent<v8::Value> context, - void*) -{ + void*) { for (ContextList::iterator it = GetContexts().begin(); it != GetContexts().end(); ++it) { if ((*it)->context == context) { @@ -167,7 +176,7 @@ static void ContextWeakReferenceCallback(v8::Persistent<v8::Value> context, NOTREACHED(); } -void EventBindings::HandleContextCreated(WebFrame* frame) { +void EventBindings::HandleContextCreated(WebFrame* frame, bool content_script) { if (!bindings_registered) return; @@ -178,26 +187,46 @@ void EventBindings::HandleContextCreated(WebFrame* frame) { DCHECK(!context.IsEmpty()); DCHECK(bindings_utils::FindContext(context) == contexts.end()); - GURL url = frame->GetView()->GetMainFrame()->GetURL(); + // Figure out the URL for the toplevel frame. If the top frame is loading, + // use its provisional URL, since we get this notification before commit. + WebFrame* main_frame = frame->GetView()->GetMainFrame(); + WebKit::WebDataSource* ds = main_frame->GetProvisionalDataSource(); + if (!ds) + ds = main_frame->GetDataSource(); + GURL url = ds->request().url(); std::string extension_id; - if (url.SchemeIs(chrome::kExtensionScheme)) + if (url.SchemeIs(chrome::kExtensionScheme)) { extension_id = url.host(); + } else if (!content_script) { + // This context is a regular non-extension web page. Ignore it. We only + // care about content scripts and extension frames. + // (Unless we're in unit tests, in which case we don't care what the URL + // is). + DCHECK(frame_context == context); + if (!in_unit_tests) + return; + } v8::Persistent<v8::Context> persistent_context = v8::Persistent<v8::Context>::New(context); v8::Persistent<v8::Context> parent_context; - if (frame_context != context) { - // The new context doesn't belong to the frame: it's a content script. - DCHECK(bindings_utils::FindContext(frame_context) != contexts.end()); + if (content_script) { + DCHECK(frame_context != context); + parent_context = v8::Persistent<v8::Context>::New(frame_context); // Content script contexts can get GCed before their frame goes away, so // set up a GC callback. persistent_context.MakeWeak(NULL, &ContextWeakReferenceCallback); } + RenderView* render_view = NULL; + if (frame->GetView() && frame->GetView()->GetDelegate()) + render_view = static_cast<RenderView*>(frame->GetView()->GetDelegate()); + contexts.push_back(linked_ptr<ContextInfo>( - new ContextInfo(persistent_context, extension_id, parent_context))); + new ContextInfo(persistent_context, extension_id, parent_context, + render_view))); v8::Handle<v8::Value> argv[1]; argv[0] = v8::String::New(extension_id.c_str()); @@ -214,15 +243,18 @@ void EventBindings::HandleContextDestroyed(WebFrame* frame) { DCHECK(!context.IsEmpty()); ContextList::iterator context_iter = bindings_utils::FindContext(context); - DCHECK(context_iter != GetContexts().end()); - ::HandleContextDestroyed(context_iter, true); + if (context_iter != GetContexts().end()) + ::HandleContextDestroyed(context_iter, true); } // static void EventBindings::CallFunction(const std::string& function_name, - int argc, v8::Handle<v8::Value>* argv) { + int argc, v8::Handle<v8::Value>* argv, + RenderView* render_view) { for (ContextList::iterator it = GetContexts().begin(); it != GetContexts().end(); ++it) { + if (render_view && render_view != (*it)->render_view) + continue; CallFunctionInContext((*it)->context, function_name, argc, argv); } } diff --git a/chrome/renderer/extensions/event_bindings.h b/chrome/renderer/extensions/event_bindings.h index ea0060f..c9f81da 100644 --- a/chrome/renderer/extensions/event_bindings.h +++ b/chrome/renderer/extensions/event_bindings.h @@ -10,6 +10,7 @@ #include <string> class RenderThreadBase; +class RenderView; class WebFrame; // This class deals with the javascript bindings related to Event objects. @@ -23,14 +24,16 @@ class EventBindings { static RenderThreadBase* GetRenderThread(); // Handle a script context coming / going away. - static void HandleContextCreated(WebFrame* frame); + static void HandleContextCreated(WebFrame* frame, bool content_script); static void HandleContextDestroyed(WebFrame* frame); - // Calls the given function in each registered context which is listening - // for events. See comments on bindings_utils::CallFunctionInContext for - // more details. + // Calls the given function in each registered context which is listening for + // events. If render_view is non-NULL, only call the function in contexts + // belonging to that view. See comments on + // bindings_utils::CallFunctionInContext for more details. static void CallFunction(const std::string& function_name, int argc, - v8::Handle<v8::Value>* argv); + v8::Handle<v8::Value>* argv, + RenderView* render_view); }; #endif // CHROME_RENDERER_EXTENSIONS_EVENT_BINDINGS_H_ diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc index 35fa0a4..1e4bafe1 100644 --- a/chrome/renderer/extensions/extension_process_bindings.cc +++ b/chrome/renderer/extensions/extension_process_bindings.cc @@ -68,6 +68,8 @@ class ExtensionImpl : public ExtensionBase { return v8::FunctionTemplate::New(GetViews); } else if (name->Equals(v8::String::New("GetNextRequestId"))) { return v8::FunctionTemplate::New(GetNextRequestId); + } else if (name->Equals(v8::String::New("OpenChannelToTab"))) { + return v8::FunctionTemplate::New(OpenChannelToTab); } else if (name->Equals(v8::String::New("GetCurrentPageActions"))) { return v8::FunctionTemplate::New(GetCurrentPageActions); } else if (name->Equals(v8::String::New("StartRequest"))) { @@ -110,6 +112,28 @@ class ExtensionImpl : public ExtensionBase { return v8::Integer::New(next_request_id++); } + // Creates a new messaging channel to the tab with the given ID. + static v8::Handle<v8::Value> OpenChannelToTab(const v8::Arguments& args) { + // Get the current RenderView so that we can send a routed IPC message from + // the correct source. + RenderView* renderview = bindings_utils::GetRenderViewForCurrentContext(); + if (!renderview) + return v8::Undefined(); + + if (args.Length() >= 3 && args[0]->IsInt32() && args[1]->IsString() && + args[2]->IsString()) { + int tab_id = args[0]->Int32Value(); + std::string extension_id = *v8::String::Utf8Value(args[1]->ToString()); + std::string channel_name = *v8::String::Utf8Value(args[2]->ToString()); + int port_id = -1; + renderview->Send(new ViewHostMsg_OpenChannelToTab( + renderview->routing_id(), tab_id, extension_id, channel_name, + &port_id)); + return v8::Integer::New(port_id); + } + return v8::Undefined(); + } + static v8::Handle<v8::Value> GetCurrentPageActions( const v8::Arguments& args) { std::string extension_id = ExtensionIdFromCurrentContext(); diff --git a/chrome/renderer/extensions/renderer_extension_bindings.cc b/chrome/renderer/extensions/renderer_extension_bindings.cc index 37767f3..9de9035 100644 --- a/chrome/renderer/extensions/renderer_extension_bindings.cc +++ b/chrome/renderer/extensions/renderer_extension_bindings.cc @@ -154,8 +154,9 @@ v8::Extension* RendererExtensionBindings::Get() { } void RendererExtensionBindings::Invoke(const std::string& function_name, - const ListValue& args) { + const ListValue& args, + RenderView* renderview) { v8::HandleScope handle_scope; std::vector< v8::Handle<v8::Value> > argv = ListValueToV8(args); - EventBindings::CallFunction(function_name, argv.size(), &argv[0]); + EventBindings::CallFunction(function_name, argv.size(), &argv[0], renderview); } diff --git a/chrome/renderer/extensions/renderer_extension_bindings.h b/chrome/renderer/extensions/renderer_extension_bindings.h index 5196807..89e903d 100644 --- a/chrome/renderer/extensions/renderer_extension_bindings.h +++ b/chrome/renderer/extensions/renderer_extension_bindings.h @@ -10,7 +10,7 @@ #include <string> class ListValue; -class RenderThreadBase; +class RenderView; // This class adds extension-related javascript bindings to a renderer. It is // used by both web renderers and extension processes. @@ -23,7 +23,8 @@ class RendererExtensionBindings { static v8::Extension* Get(); // Call the given javascript function with the specified arguments. - static void Invoke(const std::string& function_name, const ListValue& args); + static void Invoke(const std::string& function_name, const ListValue& args, + RenderView* renderview); }; #endif // CHROME_RENDERER_EXTENSIONS_RENDERER_EXTENSION_BINDINGS_H_ diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index 3debfac..e35377a 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -400,7 +400,7 @@ void RenderThread::EnsureWebKitInitialized() { void RenderThread::OnExtensionMessageInvoke(const std::string& function_name, const ListValue& args) { - RendererExtensionBindings::Invoke(function_name, args); + RendererExtensionBindings::Invoke(function_name, args, NULL); } void RenderThread::OnPurgePluginListCache() { diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index a7445c0..0052e1e 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -38,6 +38,7 @@ #include "chrome/renderer/devtools_client.h" #include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/extensions/extension_process_bindings.h" +#include "chrome/renderer/extensions/renderer_extension_bindings.h" #include "chrome/renderer/localized_error.h" #include "chrome/renderer/media/audio_renderer_impl.h" #include "chrome/renderer/media/buffered_data_source.h" @@ -415,6 +416,8 @@ void RenderView::OnMessageReceived(const IPC::Message& message) { OnPopupNotificationVisibilityChanged) IPC_MESSAGE_HANDLER(ViewMsg_MoveOrResizeStarted, OnMoveOrResizeStarted) IPC_MESSAGE_HANDLER(ViewMsg_ExtensionResponse, OnExtensionResponse) + IPC_MESSAGE_HANDLER(ViewMsg_ExtensionMessageInvoke, + OnExtensionMessageInvoke) IPC_MESSAGE_HANDLER(ViewMsg_ClearFocusedNode, OnClearFocusedNode) IPC_MESSAGE_HANDLER(ViewMsg_SetBackground, OnSetBackground) IPC_MESSAGE_HANDLER(ViewMsg_EnableIntrinsicWidthChangedMode, @@ -1471,7 +1474,7 @@ void RenderView::DocumentElementAvailable(WebFrame* frame) { } void RenderView::DidCreateScriptContextForFrame(WebFrame* webframe) { - EventBindings::HandleContextCreated(webframe); + EventBindings::HandleContextCreated(webframe, false); } void RenderView::DidDestroyScriptContextForFrame(WebFrame* webframe) { @@ -1479,7 +1482,7 @@ void RenderView::DidDestroyScriptContextForFrame(WebFrame* webframe) { } void RenderView::DidCreateIsolatedScriptContext(WebFrame* webframe) { - EventBindings::HandleContextCreated(webframe); + EventBindings::HandleContextCreated(webframe, true); } WebNavigationPolicy RenderView::PolicyForNavigationAction( @@ -2855,6 +2858,11 @@ void RenderView::OnExtensionResponse(int request_id, request_id, success, response, error); } +void RenderView::OnExtensionMessageInvoke(const std::string& function_name, + const ListValue& args) { + RendererExtensionBindings::Invoke(function_name, args, this); +} + // Dump all load time histograms. // // There are 13 histograms measuring various times. diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index 7cc65cf..9d8705a 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -47,6 +47,7 @@ class DevToolsAgent; class DevToolsClient; class FilePath; class GURL; +class ListValue; class NavigationState; class PrintWebViewHelper; class WebFrame; @@ -544,6 +545,9 @@ class RenderView : public RenderWidget, webkit_glue::WebAccessibility::OutParams* out_params); void OnClearAccessibilityInfo(int acc_obj_id, bool clear_all); + void OnExtensionMessageInvoke(const std::string& function_name, + const ListValue& args); + void OnMoveOrResizeStarted(); // Checks if the RenderView should close, runs the beforeunload handler and diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd index 6be873d..ac2e6de 100644 --- a/chrome/renderer/renderer_resources.grd +++ b/chrome/renderer/renderer_resources.grd @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- This comment is only here because changes to resources are not picked up -without changes to the corresponding grd file. yipeee! --> +without changes to the corresponding grd file. mp2 --> <grit latest_public_release="0" current_release="1"> <outputs> <output filename="grit/renderer_resources.h" type="rc_header"> diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js index b9a0548..e702744 100644 --- a/chrome/renderer/resources/extension_process_bindings.js +++ b/chrome/renderer/resources/extension_process_bindings.js @@ -17,6 +17,7 @@ var chrome = chrome || {}; native function GetViews(); native function GetChromeHidden(); native function GetNextRequestId(); + native function OpenChannelToTab(); if (!chrome) chrome = {}; @@ -304,6 +305,17 @@ var chrome = chrome || {}; chrome.types.optFun ]; + chrome.tabs.connect = function(tabId, opt_name) { + validate(arguments, arguments.callee.params); + var portId = OpenChannelToTab(tabId, chrome.extension.id_, opt_name || ""); + return chromeHidden.Port.createPort(portId, opt_name); + }; + + chrome.tabs.connect.params = [ + chrome.types.pInt, + chrome.types.optStr + ]; + // Sends ({Tab}). // Will *NOT* be followed by tab-attached - it is implied. // *MAY* be followed by tab-selection-changed. @@ -531,7 +543,7 @@ var chrome = chrome || {}; chrome.extension = new chrome.Extension(extensionId); // TODO(mpcomplete): self.onConnect is deprecated. Remove it at 1.0. // http://code.google.com/p/chromium/issues/detail?id=16356 - chrome.self.onConnect = new chrome.Event("channel-connect:" + extensionId); + chrome.self.onConnect = chrome.extension.onConnect; setupPageActionEvents(extensionId); }); diff --git a/chrome/renderer/user_script_slave.cc b/chrome/renderer/user_script_slave.cc index 48017c1..088f409 100644 --- a/chrome/renderer/user_script_slave.cc +++ b/chrome/renderer/user_script_slave.cc @@ -160,7 +160,10 @@ bool UserScriptSlave::InjectScripts(WebFrame* frame, StringPrintf(kInitExtension, script->extension_id().c_str())))); } - frame->ExecuteScriptInNewWorld(&sources.front(), sources.size()); + // TODO(abarth): switch back to NewWorld when V8IsolatedWorld is fixed. + // https://bugs.webkit.org/show_bug.cgi?id=27397 + frame->ExecuteScriptInNewContext(&sources.front(), sources.size()); + // frame->ExecuteScriptInNewWorld(&sources.front(), sources.size()); } } diff --git a/chrome/test/testing_profile.h b/chrome/test/testing_profile.h index 43b6eef..0794e3c 100644 --- a/chrome/test/testing_profile.h +++ b/chrome/test/testing_profile.h @@ -86,6 +86,9 @@ class TestingProfile : public Profile { virtual ExtensionProcessManager* GetExtensionProcessManager() { return NULL; } + virtual ExtensionMessageService* GetExtensionMessageService() { + return NULL; + } virtual SSLHostState* GetSSLHostState() { return NULL; } |