diff options
author | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-13 20:38:56 +0000 |
---|---|---|
committer | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-07-13 20:38:56 +0000 |
commit | 2933580fcac85dbd0ad6eb5ccb5fe88fef40a128 (patch) | |
tree | d79b615ee353f0f6e90668e00aae824894853f04 | |
parent | cd7b299fd6053afcd625ce36f0e51f94c83b182c (diff) | |
download | chromium_src-2933580fcac85dbd0ad6eb5ccb5fe88fef40a128.zip chromium_src-2933580fcac85dbd0ad6eb5ccb5fe88fef40a128.tar.gz chromium_src-2933580fcac85dbd0ad6eb5ccb5fe88fef40a128.tar.bz2 |
Add an optional channel name to the extension message connect event.
Also changed the way port IDs work internally to reduce confusion. Each end of the port has its own ID, not the ID of its opposite end.
BUG=13706
TEST=no
Review URL: http://codereview.chromium.org/155381
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20519 0039d316-1c4b-4281-b951-d872f2087c98
-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 |