diff options
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/extensions/extension_message_service.cc | 52 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_message_service.h | 40 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_messages_unittest.cc | 28 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_message_filter.cc | 5 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_message_filter.h | 4 | ||||
-rw-r--r-- | chrome/common/render_messages_internal.h | 10 | ||||
-rw-r--r-- | chrome/renderer/extensions/renderer_extension_bindings.cc | 5 | ||||
-rw-r--r-- | chrome/renderer/mock_render_thread.cc | 5 | ||||
-rw-r--r-- | chrome/renderer/mock_render_thread.h | 6 | ||||
-rw-r--r-- | chrome/renderer/renderer_resources.grd | 2 | ||||
-rw-r--r-- | chrome/renderer/resources/extension_process_bindings.js | 3 | ||||
-rw-r--r-- | chrome/renderer/resources/renderer_extension_bindings.js | 25 |
12 files changed, 118 insertions, 67 deletions
diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc index 78dd1f2..70bfda7 100644 --- a/chrome/browser/extensions/extension_message_service.cc +++ b/chrome/browser/extensions/extension_message_service.cc @@ -42,13 +42,15 @@ struct SingletonData { InstanceMap map; }; -static void DispatchOnConnect(IPC::Message::Sender* channel, int source_port_id, +static void DispatchOnConnect(IPC::Message::Sender* channel, int dest_port_id, + const std::string& channel_name, const std::string& tab_json, const std::string& extension_id) { ListValue args; - args.Set(0, Value::CreateIntegerValue(source_port_id)); - args.Set(1, Value::CreateStringValue(tab_json)); - args.Set(2, Value::CreateStringValue(extension_id)); + args.Set(0, Value::CreateIntegerValue(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)); } @@ -172,7 +174,7 @@ void ExtensionMessageService::AllocatePortIdPair(int* port1, int* port2) { int ExtensionMessageService::OpenChannelToExtension( int routing_id, const std::string& extension_id, - ResourceMessageFilter* source) { + const std::string& channel_name, ResourceMessageFilter* source) { DCHECK_EQ(MessageLoop::current(), ChromeThread::GetMessageLoop(ChromeThread::IO)); DCHECK(initialized_); @@ -182,24 +184,29 @@ int ExtensionMessageService::OpenChannelToExtension( 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::OpenChannelOnUIThread, - routing_id, port1_id, source->GetProcessId(), extension_id)); + routing_id, port2_id, source->GetProcessId(), extension_id, + channel_name)); - return port2_id; + return port1_id; } void ExtensionMessageService::OpenChannelOnUIThread( - int source_routing_id, int source_port_id, int source_process_id, - const std::string& extension_id) { + int source_routing_id, int receivers_port_id, int source_process_id, + const std::string& extension_id, const std::string& channel_name) { RenderProcessHost* source = RenderProcessHost::FromID(source_process_id); - OpenChannelOnUIThreadImpl(source_routing_id, source_port_id, - source_process_id, source, extension_id); + OpenChannelOnUIThreadImpl(source_routing_id, receivers_port_id, + source_process_id, source, extension_id, + channel_name); } void ExtensionMessageService::OpenChannelOnUIThreadImpl( - int source_routing_id, int source_port_id, int source_process_id, - IPC::Message::Sender* source, const std::string& extension_id) { + int source_routing_id, int receivers_port_id, int source_process_id, + IPC::Message::Sender* source, const std::string& extension_id, + const std::string& channel_name) { DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); if (!source) @@ -224,7 +231,7 @@ void ExtensionMessageService::OpenChannelOnUIThreadImpl( return; } - channels_[GET_CHANNEL_ID(source_port_id)] = channel; + channels_[GET_CHANNEL_ID(receivers_port_id)] = channel; // Include info about the opener's tab (if it was a tab). std::string tab_json = "null"; @@ -239,7 +246,8 @@ void ExtensionMessageService::OpenChannelOnUIThreadImpl( // port ID (the opener has the opposite port ID). for (MessageChannel::Ports::iterator it = channel->receivers.begin(); it != channel->receivers.end(); ++it) { - DispatchOnConnect(*it, source_port_id, tab_json, extension_id); + DispatchOnConnect(*it, receivers_port_id, channel_name, tab_json, + extension_id); } } @@ -259,10 +267,10 @@ 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, port1_id, source_process_id, - source, extension_id); + OpenChannelOnUIThreadImpl(routing_id, port2_id, source_process_id, + source, extension_id, ""); - return port2_id; + return port1_id; } void ExtensionMessageService::CloseChannel(int port_id) { @@ -292,23 +300,23 @@ void ExtensionMessageService::CloseChannelImpl( } void ExtensionMessageService::PostMessageFromRenderer( - int dest_port_id, const std::string& message) { + int source_port_id, const std::string& message) { DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); MessageChannelMap::iterator iter = - channels_.find(GET_CHANNEL_ID(dest_port_id)); + channels_.find(GET_CHANNEL_ID(source_port_id)); if (iter == channels_.end()) return; // 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; - int source_port_id = GET_OPPOSITE_PORT_ID(dest_port_id); for (MessageChannel::Ports::iterator it = ports->begin(); it != ports->end(); ++it) { - DispatchOnMessage(*it, message, source_port_id); + DispatchOnMessage(*it, message, dest_port_id); } } diff --git a/chrome/browser/extensions/extension_message_service.h b/chrome/browser/extensions/extension_message_service.h index 7a87adf..77970bc 100644 --- a/chrome/browser/extensions/extension_message_service.h +++ b/chrome/browser/extensions/extension_message_service.h @@ -19,15 +19,27 @@ class RenderProcessHost; class ResourceMessageFilter; class URLRequestContext; -// This class manages message passing between renderer processes. It maintains -// a list of available extensions and which renderers each lives in, as well as -// a set of open channels. +// 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. +// +// 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. +// - 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 of which belongs to an extension) -// port: an IPC::Message::Sender interface through which we communicate to a -// process. We use MessageFilters for this since that allows us to send our -// messages on the IO thread. +// 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 { public: // Javascript function name constants. @@ -35,6 +47,7 @@ class ExtensionMessageService : public NotificationObserver { static const char kDispatchOnDisconnect[]; static const char kDispatchOnMessage[]; 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. @@ -78,11 +91,13 @@ class ExtensionMessageService : public NotificationObserver { // --- IO thread only: // Given an extension's ID, opens a channel between the given renderer "port" - // and that extension. Returns a channel ID to be used for posting messages - // between the processes, or -1 if the extension doesn't exist. + // and every listening context owned by that extension. Returns a port ID + // to be used for posting messages between the processes. |channel_name| is + // an optional identifier for use by extension developers. // This runs on the IO thread so that it can be used in a synchronous IPC // message. int OpenChannelToExtension(int routing_id, const std::string& extension_id, + const std::string& channel_name, ResourceMessageFilter* source); private: @@ -110,16 +125,17 @@ class ExtensionMessageService : public NotificationObserver { // --- UI thread only: - // Handles channel creation and notifies the destination that a channel was + // 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& 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); + IPC::Message::Sender* source, const std::string& extension_id, + const std::string& channel_name); NotificationRegistrar registrar_; diff --git a/chrome/browser/extensions/extension_messages_unittest.cc b/chrome/browser/extensions/extension_messages_unittest.cc index 27a5bd9..c2e4540 100644 --- a/chrome/browser/extensions/extension_messages_unittest.cc +++ b/chrome/browser/extensions/extension_messages_unittest.cc @@ -9,11 +9,13 @@ #include "chrome/test/render_view_test.h" #include "testing/gtest/include/gtest/gtest.h" -static void DispatchOnConnect(int source_port_id, const std::string& tab_json) { +static void DispatchOnConnect(int source_port_id, const std::string& name, + const std::string& tab_json) { ListValue args; args.Set(0, Value::CreateIntegerValue(source_port_id)); - args.Set(1, Value::CreateStringValue(tab_json)); - args.Set(2, Value::CreateStringValue("")); // extension ID is empty for tests + args.Set(1, Value::CreateStringValue(name)); + args.Set(2, Value::CreateStringValue(tab_json)); + args.Set(3, Value::CreateStringValue("")); // extension ID is empty for tests RendererExtensionBindings::Invoke( ExtensionMessageService::kDispatchOnConnect, args); } @@ -40,7 +42,7 @@ TEST_F(RenderViewTest, ExtensionMessagesOpenChannel) { LoadHTML("<body></body>"); ExecuteJavaScript( "var e = new chrome.Extension('foobar');" - "var port = e.connect();" + "var port = e.connect('testName');" "port.onMessage.addListener(doOnMessage);" "port.postMessage({message: 'content ready'});" "function doOnMessage(msg, port) {" @@ -51,7 +53,11 @@ TEST_F(RenderViewTest, ExtensionMessagesOpenChannel) { const IPC::Message* open_channel_msg = render_thread_.sink().GetUniqueMessageMatching( ViewHostMsg_OpenChannelToExtension::ID); - EXPECT_TRUE(open_channel_msg); + ASSERT_TRUE(open_channel_msg); + void* iter = IPC::SyncMessage::GetDataIterator(open_channel_msg); + ViewHostMsg_OpenChannelToExtension::SendParam open_params; + ASSERT_TRUE(IPC::ReadParam(open_channel_msg, &iter, &open_params)); + EXPECT_EQ("testName", open_params.c); const IPC::Message* post_msg = render_thread_.sink().GetUniqueMessageMatching( @@ -71,7 +77,7 @@ TEST_F(RenderViewTest, ExtensionMessagesOpenChannel) { render_thread_.sink().GetUniqueMessageMatching( ViewHostMsg_RunJavaScriptMessage::ID); ASSERT_TRUE(alert_msg); - void* iter = IPC::SyncMessage::GetDataIterator(alert_msg); + iter = IPC::SyncMessage::GetDataIterator(alert_msg); ViewHostMsg_RunJavaScriptMessage::SendParam alert_param; ASSERT_TRUE(IPC::ReadParam(alert_msg, &iter, &alert_param)); EXPECT_EQ(L"content got: 42", alert_param.a); @@ -86,7 +92,8 @@ TEST_F(RenderViewTest, ExtensionMessagesOnConnect) { " port.test = 24;" " port.onMessage.addListener(doOnMessage);" " port.onDisconnect.addListener(doOnDisconnect);" - " port.postMessage({message: 'onconnect from ' + port.tab.url});" + " port.postMessage({message: 'onconnect from ' + port.tab.url + " + " ' name ' + port.name});" "});" "function doOnMessage(msg, port) {" " alert('got: ' + msg.val);" @@ -99,7 +106,8 @@ TEST_F(RenderViewTest, ExtensionMessagesOnConnect) { // Simulate a new connection being opened. const int kPortId = 0; - DispatchOnConnect(kPortId, "{\"url\":\"foo://bar\"}"); + const std::string kPortName = "testName"; + DispatchOnConnect(kPortId, kPortName, "{\"url\":\"foo://bar\"}"); // Verify that we handled the new connection by posting a message. const IPC::Message* post_msg = @@ -108,7 +116,9 @@ TEST_F(RenderViewTest, ExtensionMessagesOnConnect) { ASSERT_TRUE(post_msg); ViewHostMsg_ExtensionPostMessage::Param post_params; ViewHostMsg_ExtensionPostMessage::Read(post_msg, &post_params); - EXPECT_EQ("{\"message\":\"onconnect from foo://bar\"}", post_params.b); + std::string expected_msg = + "{\"message\":\"onconnect from foo://bar name " + kPortName + "\"}"; + EXPECT_EQ(expected_msg, post_params.b); // Now simulate getting a message back from the channel opener. render_thread_.sink().ClearMessages(); diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index 6858430..d913aec 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -884,9 +884,10 @@ void ResourceMessageFilter::OnFreeTransportDIB( #endif void ResourceMessageFilter::OnOpenChannelToExtension( - int routing_id, const std::string& extension_id, int* port_id) { + 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, this); + OpenChannelToExtension(routing_id, extension_id, channel_name, this); } bool ResourceMessageFilter::CheckBenchmarkingEnabled() { diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index 70eeaf8..613dd77 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -218,8 +218,8 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, TransportDIB::Handle* result); void OnFreeTransportDIB(TransportDIB::Id dib_id); - void OnOpenChannelToExtension(int routing_id, - const std::string& extension_id, int* port_id); + void OnOpenChannelToExtension(int routing_id, const std::string& extension_id, + const std::string& channel_name, int* port_id); void OnCloseIdleConnections(); void OnSetCacheMode(bool enabled); diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index ee3d154..31670ba 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -1403,12 +1403,14 @@ IPC_BEGIN_MESSAGES(ViewHost) IPC_MESSAGE_CONTROL1(ViewHostMsg_ForwardToWorker, IPC::Message /* message */) - // Get a port handle to a currently-running extension process for the - // extension with the given ID. If no such extension is found, -1 is - // returned. The handle can be used for sending messages to the extension. - IPC_SYNC_MESSAGE_CONTROL2_1(ViewHostMsg_OpenChannelToExtension, + // Open a channel to all listening contexts owned by the extension with + // the given ID. This always returns a valid port ID which can be used for + // sending messages. If an error occurred, the opener will be notified + // asynchronously. + IPC_SYNC_MESSAGE_CONTROL3_1(ViewHostMsg_OpenChannelToExtension, int /* routing_id */, std::string /* extension_id */, + std::string /* channel_name */, int /* port_id */) // Get a port handle to the given tab's process. The handle can be used for diff --git a/chrome/renderer/extensions/renderer_extension_bindings.cc b/chrome/renderer/extensions/renderer_extension_bindings.cc index 4cff1dd..37767f3 100644 --- a/chrome/renderer/extensions/renderer_extension_bindings.cc +++ b/chrome/renderer/extensions/renderer_extension_bindings.cc @@ -61,11 +61,12 @@ class ExtensionImpl : public ExtensionBase { if (!renderview) return v8::Undefined(); - if (args.Length() >= 1 && args[0]->IsString()) { + if (args.Length() >= 2 && args[0]->IsString() && args[1]->IsString()) { std::string id = *v8::String::Utf8Value(args[0]->ToString()); + std::string channel_name = *v8::String::Utf8Value(args[1]->ToString()); int port_id = -1; renderview->Send(new ViewHostMsg_OpenChannelToExtension( - renderview->routing_id(), id, &port_id)); + renderview->routing_id(), id, channel_name, &port_id)); return v8::Integer::New(port_id); } return v8::Undefined(); diff --git a/chrome/renderer/mock_render_thread.cc b/chrome/renderer/mock_render_thread.cc index 3494b12..17dab52 100644 --- a/chrome/renderer/mock_render_thread.cc +++ b/chrome/renderer/mock_render_thread.cc @@ -97,8 +97,9 @@ void MockRenderThread::OnMsgCreateWidget(int opener_id, } void MockRenderThread::OnMsgOpenChannelToExtension( - int routing_id, const std::string& extension_id, int* channel_id) { - *channel_id = 0; + int routing_id, const std::string& extension_id, + const std::string& channel_name, int* port_id) { + *port_id = 0; } void MockRenderThread::OnDuplicateSection( diff --git a/chrome/renderer/mock_render_thread.h b/chrome/renderer/mock_render_thread.h index 1921fb1..0e5bab8 100644 --- a/chrome/renderer/mock_render_thread.h +++ b/chrome/renderer/mock_render_thread.h @@ -79,9 +79,9 @@ class MockRenderThread : public RenderThreadBase { int* route_id); // The callee expects to be returned a valid channel_id. - void OnMsgOpenChannelToExtension(int routing_id, - const std::string& extension_id, - int* channel_id); + void OnMsgOpenChannelToExtension( + int routing_id, const std::string& extension_id, + const std::string& channel_name, int* port_id); void OnDuplicateSection(base::SharedMemoryHandle renderer_handle, base::SharedMemoryHandle* browser_handle); diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd index 8acec84..bdba06d 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. mp13 --> +without changes to the corresponding grd file. mp1 --> <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 51756d0..1c8fc9c4 100644 --- a/chrome/renderer/resources/extension_process_bindings.js +++ b/chrome/renderer/resources/extension_process_bindings.js @@ -7,6 +7,9 @@ // have your change take effect. // ----------------------------------------------------------------------------- +// This script contains privileged chrome extension related javascript APIs. +// It is loaded by pages whose URL has the chrome-extension protocol. + var chrome = chrome || {}; (function() { native function GetViews(); diff --git a/chrome/renderer/resources/renderer_extension_bindings.js b/chrome/renderer/resources/renderer_extension_bindings.js index 2a41d5f..d8b5a4b 100644 --- a/chrome/renderer/resources/renderer_extension_bindings.js +++ b/chrome/renderer/resources/renderer_extension_bindings.js @@ -7,6 +7,13 @@ // have your change take effect. // ----------------------------------------------------------------------------- +// This script contains unprivileged javascript APIs related to chrome +// extensions. It is loaded by any extension-related context, such as content +// scripts or toolstrips. +// See user_script_slave.cc for script that is loaded by content scripts only. +// TODO(mpcomplete): we also load this in regular web pages, but don't need +// to. + var chrome = chrome || {}; (function () { native function OpenChannelToExtension(id); @@ -21,8 +28,9 @@ var chrome = chrome || {}; // Port object. Represents a connection to another script context through // which messages can be passed. - chrome.Port = function(portId) { + chrome.Port = function(portId, opt_name) { this.portId_ = portId; + this.name = opt_name; this.onDisconnect = new chrome.Event(); this.onMessage = new chrome.Event(); }; @@ -31,11 +39,11 @@ var chrome = chrome || {}; // Hidden port creation function. We don't want to expose an API that lets // people add arbitrary port IDs to the port list. - chromeHidden.Port.createPort = function(portId) { + chromeHidden.Port.createPort = function(portId, opt_name) { if (ports[portId]) { throw new Error("Port '" + portId + "' already exists."); } - var port = new chrome.Port(portId); + var port = new chrome.Port(portId, opt_name); ports[portId] = port; chromeHidden.onUnload.addListener(function() { port.disconnect(); @@ -44,14 +52,15 @@ var chrome = chrome || {}; } // Called by native code when a channel has been opened to this context. - chromeHidden.Port.dispatchOnConnect = function(portId, tab, extensionId) { + chromeHidden.Port.dispatchOnConnect = function(portId, channelName, tab, + extensionId) { // Only create a new Port if someone is actually listening for a connection. // In addition to being an optimization, this also fixes a bug where if 2 // channels were opened to and from the same process, closing one would // close both. var connectEvent = "channel-connect:" + extensionId; if (chromeHidden.Event.hasListener(connectEvent)) { - var port = chromeHidden.Port.createPort(portId); + var port = chromeHidden.Port.createPort(portId, channelName); if (tab) { tab = JSON.parse(tab); } @@ -103,11 +112,11 @@ var chrome = chrome || {}; // Opens a message channel to the extension. Returns a Port for // message passing. - chrome.Extension.prototype.connect = function() { - var portId = OpenChannelToExtension(this.id_); + chrome.Extension.prototype.connect = function(opt_name) { + var portId = OpenChannelToExtension(this.id_, opt_name || ""); if (portId == -1) throw new Error("No such extension: '" + this.id_ + "'"); - return chromeHidden.Port.createPort(portId); + return chromeHidden.Port.createPort(portId, opt_name); }; // Returns a resource URL that can be used to fetch a resource from this |