summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-20 21:05:37 +0000
committermpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-20 21:05:37 +0000
commit7120f1327bcd3f0c33a983ee4a61c277747bb566 (patch)
treeda00ed514530587dd14f76941063d34a4afcda88
parent6d33519bd4d811112cef3d64d9c346531d65ab9b (diff)
downloadchromium_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
-rw-r--r--chrome/browser/automation/automation_provider.cc6
-rw-r--r--chrome/browser/automation/extension_port_container.cc3
-rw-r--r--chrome/browser/automation/extension_port_container.h3
-rw-r--r--chrome/browser/extensions/extension_bookmarks_module.cc6
-rw-r--r--chrome/browser/extensions/extension_browser_event_router.cc6
-rw-r--r--chrome/browser/extensions/extension_function_dispatcher.cc5
-rw-r--r--chrome/browser/extensions/extension_message_service.cc278
-rw-r--r--chrome/browser/extensions/extension_message_service.h103
-rw-r--r--chrome/browser/extensions/extension_messages_unittest.cc6
-rw-r--r--chrome/browser/extensions/extension_process_manager.cc13
-rw-r--r--chrome/browser/extensions/extension_process_manager.h11
-rw-r--r--chrome/browser/profile.cc12
-rw-r--r--chrome/browser/profile.h7
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.cc17
-rw-r--r--chrome/browser/renderer_host/render_view_host.cc7
-rw-r--r--chrome/browser/renderer_host/render_widget_host.h3
-rw-r--r--chrome/browser/renderer_host/resource_message_filter.cc22
-rw-r--r--chrome/browser/renderer_host/resource_message_filter.h8
-rw-r--r--chrome/common/render_messages_internal.h19
-rw-r--r--chrome/renderer/extensions/bindings_utils.h9
-rw-r--r--chrome/renderer/extensions/event_bindings.cc56
-rw-r--r--chrome/renderer/extensions/event_bindings.h13
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.cc24
-rw-r--r--chrome/renderer/extensions/renderer_extension_bindings.cc5
-rw-r--r--chrome/renderer/extensions/renderer_extension_bindings.h5
-rw-r--r--chrome/renderer/render_thread.cc2
-rw-r--r--chrome/renderer/render_view.cc12
-rw-r--r--chrome/renderer/render_view.h4
-rw-r--r--chrome/renderer/renderer_resources.grd2
-rw-r--r--chrome/renderer/resources/extension_process_bindings.js14
-rw-r--r--chrome/renderer/user_script_slave.cc5
-rw-r--r--chrome/test/testing_profile.h3
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;
}