summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/extensions/extension_browsertests_misc.cc132
-rw-r--r--chrome/browser/extensions/extension_function_dispatcher.cc9
-rw-r--r--chrome/browser/extensions/extension_message_service.cc174
-rw-r--r--chrome/browser/extensions/extension_message_service.h66
-rw-r--r--chrome/browser/extensions/extension_messages_unittest.cc1
-rw-r--r--chrome/browser/extensions/extensions_service_unittest.cc2
-rw-r--r--chrome/common/render_messages_internal.h11
-rw-r--r--chrome/renderer/extensions/bindings_utils.cc28
-rw-r--r--chrome/renderer/extensions/bindings_utils.h4
-rw-r--r--chrome/renderer/extensions/event_bindings.cc29
-rw-r--r--chrome/renderer/extensions/event_bindings.h5
-rw-r--r--chrome/renderer/extensions/extension_api_client_unittest.cc5
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.cc61
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.h7
-rw-r--r--chrome/renderer/render_view.cc4
-rw-r--r--chrome/renderer/renderer_resources.grd2
-rw-r--r--chrome/renderer/resources/event_bindings.js44
-rw-r--r--chrome/renderer/resources/extension_process_bindings.js43
-rw-r--r--chrome/renderer/resources/renderer_extension_bindings.js32
-rw-r--r--chrome/renderer/user_script_slave.cc5
-rw-r--r--chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/background.html38
-rw-r--r--chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/manifest.json9
-rw-r--r--chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/page.html1
-rw-r--r--chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/page.js31
-rw-r--r--chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json7
25 files changed, 457 insertions, 293 deletions
diff --git a/chrome/browser/extensions/extension_browsertests_misc.cc b/chrome/browser/extensions/extension_browsertests_misc.cc
index b594477..0211b96 100644
--- a/chrome/browser/extensions/extension_browsertests_misc.cc
+++ b/chrome/browser/extensions/extension_browsertests_misc.cc
@@ -17,8 +17,29 @@
#include "chrome/browser/views/frame/browser_view.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension_error_reporter.h"
+#include "chrome/common/notification_service.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/ui_test_utils.h"
+#include "net/base/net_util.h"
+
+// Looks for an ExtensionHost whose URL has the given path component (including
+// leading slash). Also verifies that the expected number of hosts are loaded.
+static ExtensionHost* FindHostWithPath(ExtensionProcessManager* manager,
+ const std::string& path,
+ int expected_hosts) {
+ ExtensionHost* host = NULL;
+ int num_hosts = 0;
+ for (ExtensionProcessManager::const_iterator iter = manager->begin();
+ iter != manager->end(); ++iter) {
+ if ((*iter)->GetURL().path() == path) {
+ EXPECT_FALSE(host);
+ host = *iter;
+ }
+ num_hosts++;
+ }
+ EXPECT_EQ(expected_hosts, num_hosts);
+ return host;
+}
// Tests that toolstrips initializes properly and can run basic extension js.
IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, Toolstrip) {
@@ -31,17 +52,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, Toolstrip) {
// extension has two toolstrips. Find the one that is hosting toolstrip1.html.
ExtensionProcessManager* manager =
browser()->profile()->GetExtensionProcessManager();
- ExtensionHost* host = NULL;
- int num_hosts = 0;
- for (ExtensionProcessManager::const_iterator iter = manager->begin();
- iter != manager->end(); ++iter) {
- if ((*iter)->GetURL().path() == "/toolstrip1.html") {
- ASSERT_FALSE(host);
- host = *iter;
- }
- num_hosts++;
- }
- EXPECT_EQ(2, num_hosts);
+ ExtensionHost* host = FindHostWithPath(manager, "/toolstrip1.html", 2);
// Tell it to run some JavaScript that tests that basic extension code works.
bool result = false;
@@ -105,3 +116,102 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TabContents) {
L"testTabsAPI()", &result);
EXPECT_TRUE(result);
}
+
+// Tests that message passing between extensions and tabs works.
+IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, MessagingExtensionTab) {
+ ASSERT_TRUE(LoadExtension(
+ test_data_dir_.AppendASCII("good").AppendASCII("Extensions")
+ .AppendASCII("bjafgdebaacbbbecmhlhpofkepfkgcpa")
+ .AppendASCII("1.0")));
+
+ // Get the ExtensionHost that is hosting our background page.
+ ExtensionProcessManager* manager =
+ browser()->profile()->GetExtensionProcessManager();
+ ExtensionHost* host = FindHostWithPath(manager, "/background.html", 1);
+
+ // Load the tab that will communicate with our background page.
+ ui_test_utils::NavigateToURL(
+ browser(),
+ GURL("chrome-extension://bjafgdebaacbbbecmhlhpofkepfkgcpa/page.html"));
+
+ // First test that tab->extension messaging works.
+ bool result = false;
+ ui_test_utils::ExecuteJavaScriptAndExtractBool(
+ browser()->GetSelectedTabContents()->render_view_host(), L"",
+ L"testPostMessageFromTab()", &result);
+ EXPECT_TRUE(result);
+
+ // Now test extension->tab messaging, with disconnect events.
+ result = false;
+ ui_test_utils::ExecuteJavaScriptAndExtractBool(
+ host->render_view_host(), L"", L"testDisconnect()", &result);
+ EXPECT_TRUE(result);
+
+ result = false;
+ ui_test_utils::ExecuteJavaScriptAndExtractBool(
+ host->render_view_host(), L"", L"testPostMessage()", &result);
+ EXPECT_TRUE(result);
+
+ result = false;
+ ui_test_utils::ExecuteJavaScriptAndExtractBool(
+ host->render_view_host(), L"", L"testDisconnectOnClose()", &result);
+ EXPECT_TRUE(result);
+}
+
+// TODO(mpcomplete): reenable this when content script messaging is fixed:
+// http://code.google.com/p/chromium/issues/detail?id=16228.
+#if 0
+// Tests that message passing between extensions and content scripts works.
+IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, MessagingContentScript) {
+ ASSERT_TRUE(LoadExtension(
+ test_data_dir_.AppendASCII("good").AppendASCII("Extensions")
+ .AppendASCII("bjafgdebaacbbbecmhlhpofkepfkgcpa")
+ .AppendASCII("1.0")));
+
+ UserScriptMaster* master = browser()->profile()->GetUserScriptMaster();
+ if (!master->ScriptsReady()) {
+ // Wait for UserScriptMaster to finish its scan.
+ NotificationRegistrar registrar;
+ registrar.Add(this, NotificationType::USER_SCRIPTS_UPDATED,
+ NotificationService::AllSources());
+ ui_test_utils::RunMessageLoop();
+ }
+ ASSERT_TRUE(master->ScriptsReady());
+
+ // Get the ExtensionHost that is hosting our background page.
+ ExtensionProcessManager* manager =
+ browser()->profile()->GetExtensionProcessManager();
+ ExtensionHost* host = FindHostWithPath(manager, "/background.html", 1);
+
+ // Load the tab whose content script will communicate with our background
+ // page.
+ FilePath test_file;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_file);
+ test_file = test_file.AppendASCII("extensions")
+ .AppendASCII("test_file.html");
+ ui_test_utils::NavigateToURL(browser(), net::FilePathToFileURL(test_file));
+
+ // First test that tab->extension messaging works.
+ bool result = false;
+ ui_test_utils::ExecuteJavaScriptAndExtractBool(
+ browser()->GetSelectedTabContents()->render_view_host(), L"",
+ L"testPostMessageFromTab()", &result);
+ EXPECT_TRUE(result);
+
+ // Now test extension->tab messaging, with disconnect events.
+ result = false;
+ ui_test_utils::ExecuteJavaScriptAndExtractBool(
+ host->render_view_host(), L"", L"testDisconnect()", &result);
+ EXPECT_TRUE(result);
+
+ result = false;
+ ui_test_utils::ExecuteJavaScriptAndExtractBool(
+ host->render_view_host(), L"", L"testPostMessage()", &result);
+ EXPECT_TRUE(result);
+
+ result = false;
+ ui_test_utils::ExecuteJavaScriptAndExtractBool(
+ host->render_view_host(), L"", L"testDisconnectOnClose()", &result);
+ EXPECT_TRUE(result);
+}
+#endif \ No newline at end of file
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc
index 24eacf2..70cb59e 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.cc
+++ b/chrome/browser/extensions/extension_function_dispatcher.cc
@@ -183,12 +183,9 @@ ExtensionFunctionDispatcher::ExtensionFunctionDispatcher(
url_(url),
ALLOW_THIS_IN_INITIALIZER_LIST(peer_(new Peer(this))) {
all_instances()->insert(this);
- RenderProcessHost* process = render_view_host_->process();
- ExtensionMessageService* message_service =
- ExtensionMessageService::GetInstance(profile()->GetRequestContext());
- DCHECK(process);
- DCHECK(message_service);
- message_service->RegisterExtension(extension_id(), process->pid());
+
+ // Ensure the message service is initialized.
+ ExtensionMessageService::GetInstance(profile()->GetRequestContext())->Init();
}
ExtensionFunctionDispatcher::~ExtensionFunctionDispatcher() {
diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc
index 4929eea..78dd1f2 100644
--- a/chrome/browser/extensions/extension_message_service.cc
+++ b/chrome/browser/extensions/extension_message_service.cc
@@ -23,11 +23,11 @@
// Since we have 2 ports for every channel, we just index channels by half the
// port ID.
#define GET_CHANNEL_ID(port_id) ((port_id) / 2)
-#define GET_CHANNEL_PORT1(channel_id) ((channel_id) * 2)
-#define GET_CHANNEL_PORT2(channel_id) ((channel_id) * 2 + 1)
+#define GET_CHANNEL_OPENER_ID(channel_id) ((channel_id) * 2)
+#define GET_CHANNEL_RECEIVERS_ID(channel_id) ((channel_id) * 2 + 1)
// Port1 is always even, port2 is always odd.
-#define IS_PORT1_ID(port_id) (((port_id) & 1) == 0)
+#define IS_OPENER_PORT_ID(port_id) (((port_id) & 1) == 0)
// 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)
@@ -80,6 +80,10 @@ static void DispatchEvent(IPC::Message::Sender* channel,
ExtensionMessageService::kDispatchEvent, args));
}
+static std::string GetChannelConnectEvent(const std::string& extension_id) {
+ return StringPrintf("channel-connect:%s", extension_id.c_str());
+}
+
} // namespace
// Since ExtensionMessageService is a collection of Singletons, we don't need to
@@ -132,19 +136,6 @@ void ExtensionMessageService::Init() {
NotificationService::AllSources());
}
-void ExtensionMessageService::RegisterExtension(
- const std::string& extension_id, int render_process_id) {
- DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
-
- // Make sure we're initialized.
- Init();
-
- AutoLock lock(process_ids_lock_);
- DCHECK(process_ids_.find(extension_id) == process_ids_.end() ||
- process_ids_[extension_id] == render_process_id);
- process_ids_[extension_id] = render_process_id;
-}
-
void ExtensionMessageService::AddEventListener(std::string event_name,
int render_process_id) {
DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
@@ -166,54 +157,24 @@ void ExtensionMessageService::AllocatePortIdPair(int* port1, int* port2) {
int port1_id = next_port_id_++;
int port2_id = next_port_id_++;
- DCHECK(IS_PORT1_ID(port1_id));
+ DCHECK(IS_OPENER_PORT_ID(port1_id));
DCHECK(GET_OPPOSITE_PORT_ID(port1_id) == port2_id);
DCHECK(GET_OPPOSITE_PORT_ID(port2_id) == port1_id);
DCHECK(GET_CHANNEL_ID(port1_id) == GET_CHANNEL_ID(port2_id));
int channel_id = GET_CHANNEL_ID(port1_id);
- DCHECK(GET_CHANNEL_PORT1(channel_id) == port1_id);
- DCHECK(GET_CHANNEL_PORT2(channel_id) == port2_id);
+ DCHECK(GET_CHANNEL_OPENER_ID(channel_id) == port1_id);
+ DCHECK(GET_CHANNEL_RECEIVERS_ID(channel_id) == port2_id);
*port1 = port1_id;
*port2 = port2_id;
}
-int ExtensionMessageService::GetProcessIdForExtension(
- const std::string& extension_id) {
- AutoLock lock(process_ids_lock_);
- ProcessIDMap::iterator process_id_it = process_ids_.find(
- StringToLowerASCII(extension_id));
- if (process_id_it == process_ids_.end())
- return -1;
- return process_id_it->second;
-}
-
-RenderProcessHost* ExtensionMessageService::GetProcessForExtension(
- const std::string& extension_id) {
- DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
-
- int process_id = GetProcessIdForExtension(extension_id);
- if (process_id == -1)
- return NULL;
-
- RenderProcessHost* host = RenderProcessHost::FromID(process_id);
- DCHECK(host);
-
- return host;
-}
-
int ExtensionMessageService::OpenChannelToExtension(
int routing_id, const std::string& extension_id,
ResourceMessageFilter* source) {
DCHECK_EQ(MessageLoop::current(),
ChromeThread::GetMessageLoop(ChromeThread::IO));
-
- // Lookup the targeted extension process.
- int process_id = GetProcessIdForExtension(extension_id);
- if (process_id == -1)
- return -1;
-
DCHECK(initialized_);
// Create a channel ID for both sides of the channel.
@@ -223,37 +184,49 @@ int ExtensionMessageService::OpenChannelToExtension(
ui_loop_->PostTask(FROM_HERE,
NewRunnableMethod(this, &ExtensionMessageService::OpenChannelOnUIThread,
- routing_id, port1_id, source->GetProcessId(), port2_id, process_id,
- extension_id));
+ routing_id, port1_id, source->GetProcessId(), extension_id));
return port2_id;
}
void ExtensionMessageService::OpenChannelOnUIThread(
int source_routing_id, int source_port_id, int source_process_id,
- int dest_port_id, int dest_process_id, const std::string& extension_id) {
+ const std::string& extension_id) {
RenderProcessHost* source = RenderProcessHost::FromID(source_process_id);
OpenChannelOnUIThreadImpl(source_routing_id, source_port_id,
- source_process_id, source, dest_port_id,
- dest_process_id, extension_id);
+ source_process_id, source, extension_id);
}
void ExtensionMessageService::OpenChannelOnUIThreadImpl(
int source_routing_id, int source_port_id, int source_process_id,
- IPC::Message::Sender* source, int dest_port_id, int dest_process_id,
- const std::string& extension_id) {
+ IPC::Message::Sender* source, const std::string& extension_id) {
DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
- MessageChannel channel;
- channel.port1 = source;
- channel.port2 = RenderProcessHost::FromID(dest_process_id);
- if (!channel.port1 || !channel.port2) {
- // One of the processes could have been closed while posting this task.
+ if (!source)
+ return; // Source closed while task was 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;
}
channels_[GET_CHANNEL_ID(source_port_id)] = channel;
+ // Include info about the opener's tab (if it was a tab).
std::string tab_json = "null";
TabContents* contents = tab_util::GetTabContentsByID(source_process_id,
source_routing_id);
@@ -262,20 +235,18 @@ void ExtensionMessageService::OpenChannelOnUIThreadImpl(
JSONWriter::Write(tab_value, false, &tab_json);
}
- // Send the process the id for the opposite port.
- DispatchOnConnect(channel.port2, source_port_id, tab_json, extension_id);
+ // 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, source_port_id, 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);
-
- // Lookup the targeted extension process.
- int process_id = GetProcessIdForExtension(extension_id);
- if (process_id == -1)
- return -1;
-
DCHECK(initialized_);
int port1_id = -1;
@@ -289,7 +260,7 @@ int ExtensionMessageService::OpenAutomationChannelToExtension(
// information should be supplied by the caller for
// automation-initiated ports.
OpenChannelOnUIThreadImpl(routing_id, port1_id, source_process_id,
- source, port2_id, process_id, extension_id);
+ source, extension_id);
return port2_id;
}
@@ -304,38 +275,41 @@ void ExtensionMessageService::CloseChannel(int port_id) {
}
void ExtensionMessageService::CloseChannelImpl(
- MessageChannelMap::iterator channel_iter, int port_id) {
+ MessageChannelMap::iterator channel_iter, int closing_port_id) {
DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
// Notify the other side.
- if (port_id == GET_CHANNEL_PORT1(channel_iter->first)) {
- DispatchOnDisconnect(channel_iter->second.port2,
- GET_OPPOSITE_PORT_ID(port_id));
- } else {
- DCHECK_EQ(port_id, GET_CHANNEL_PORT2(channel_iter->first));
- DispatchOnDisconnect(channel_iter->second.port1,
- GET_OPPOSITE_PORT_ID(port_id));
+ 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));
}
channels_.erase(channel_iter);
}
void ExtensionMessageService::PostMessageFromRenderer(
- int port_id, const std::string& message) {
+ int dest_port_id, const std::string& message) {
DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI);
MessageChannelMap::iterator iter =
- channels_.find(GET_CHANNEL_ID(port_id));
+ channels_.find(GET_CHANNEL_ID(dest_port_id));
if (iter == channels_.end())
return;
- MessageChannel& channel = iter->second;
// Figure out which port the ID corresponds to.
- IPC::Message::Sender* dest =
- IS_PORT1_ID(port_id) ? channel.port1 : channel.port2;
-
- int source_port_id = GET_OPPOSITE_PORT_ID(port_id);
- DispatchOnMessage(dest, message, 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);
+ }
}
void ExtensionMessageService::DispatchEventToRenderers(
@@ -369,26 +343,24 @@ void ExtensionMessageService::Observe(NotificationType type,
RenderProcessHost* renderer = Source<RenderProcessHost>(source).ptr();
- {
- AutoLock lock(process_ids_lock_);
- for (ProcessIDMap::iterator it = process_ids_.begin();
- it != process_ids_.end(); ) {
- ProcessIDMap::iterator current = it++;
- if (current->second == renderer->pid()) {
- process_ids_.erase(current);
- }
- }
- }
-
// 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.port1 == renderer) {
- CloseChannelImpl(current, GET_CHANNEL_PORT1(current->first));
- } else if (current->second.port2 == renderer) {
- CloseChannelImpl(current, GET_CHANNEL_PORT2(current->first));
+ if (current->second->opener.count(renderer) > 0) {
+ CloseChannelImpl(current, GET_CHANNEL_OPENER_ID(current->first));
+ } else if (current->second->receivers.count(renderer) > 0) {
+ 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 e3b7ba8..7a87adf 100644
--- a/chrome/browser/extensions/extension_message_service.h
+++ b/chrome/browser/extensions/extension_message_service.h
@@ -9,6 +9,7 @@
#include <set>
#include <string>
+#include "base/linked_ptr.h"
#include "base/lock.h"
#include "chrome/common/ipc_message.h"
#include "chrome/common/notification_registrar.h"
@@ -43,12 +44,8 @@ class ExtensionMessageService : public NotificationObserver {
// --- UI thread only:
- // Gets the process for the specified extension.
- RenderProcessHost* GetProcessForExtension(const std::string& extension_id);
-
- // Register an extension and its corresponding renderer process.
- void RegisterExtension(const std::string& extension_id,
- int render_process_id);
+ // UI-thread specific initialization. Does nothing if called more than once.
+ void Init();
// Add or remove |render_process_pid| as a listener for |event_name|.
void AddEventListener(std::string event_name, int render_process_id);
@@ -87,71 +84,54 @@ class ExtensionMessageService : public NotificationObserver {
// message.
int OpenChannelToExtension(int routing_id, const std::string& extension_id,
ResourceMessageFilter* source);
-
+
private:
- // The connection between two ports. It is possible that both ports
- // refer to the same renderer.
+ // 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 {
- IPC::Message::Sender* port1;
- IPC::Message::Sender* port2;
+ typedef std::set<IPC::Message::Sender*> Ports;
+ Ports opener; // only 1 opener, but we use a set to simplify logic
+ Ports receivers;
};
// A map of channel ID to its channel object.
- typedef std::map<int, MessageChannel> MessageChannelMap;
+ typedef std::map<int, linked_ptr<MessageChannel> > MessageChannelMap;
// Allocates a pair of port ids.
// NOTE: this can be called from any thread.
void AllocatePortIdPair(int* port1, int* port2);
- // Gets the process ID for the specified extension.
- // NOTE: this can be called from any thread.
- int GetProcessIdForExtension(const std::string& extension_id);
-
void CloseChannelImpl(MessageChannelMap::iterator channel_iter, int port_id);
- int OpenChannelToExtensionImpl(const std::string& extension_id,
- IPC::Message::Sender* source);
-
- NotificationRegistrar registrar_;
-
// The UI message loop, used for posting tasks.
MessageLoop* ui_loop_;
- // A map of extension ID to the render_process_id that the extension lives in.
- typedef std::map<std::string, int> ProcessIDMap;
- ProcessIDMap process_ids_;
-
- // Protects the process_ids map, since it can be accessed on the IO thread
- // or UI thread. Be careful not to hold this lock when calling external
- // code (especially sending messages) to avoid deadlock.
- Lock process_ids_lock_;
-
- // A map between an event name and a set of process id's that are listening
- // to that event.
- typedef std::map<std::string, std::set<int> > ListenerMap;
- ListenerMap listeners_;
-
// --- UI thread only:
- // UI-thread specific initialization. Does nothing if called more than once.
- void Init();
-
// Handles channel creation and notifies the destination that a channel was
// opened.
void OpenChannelOnUIThread(int source_routing_id,
int source_port_id, int source_process_id,
- int dest_port_id, int dest_process_id,
const std::string& extension_id);
- // Common between OpenChannelOnUIThread and
- // OpenAutomationChannelToExtension.
+ // Common between OpenChannelOnUIThread and OpenAutomationChannelToExtension.
void OpenChannelOnUIThreadImpl(
int source_routing_id, int source_port_id, int source_process_id,
- IPC::Message::Sender* source, int dest_port_id, int dest_process_id,
- const std::string& extension_id);
+ IPC::Message::Sender* source, const std::string& extension_id);
+
+ NotificationRegistrar registrar_;
MessageChannelMap channels_;
+ // A map between an event name and a set of process id's that are listening
+ // to that event.
+ typedef std::map<std::string, std::set<int> > ListenerMap;
+ ListenerMap listeners_;
+
+ // --- UI or IO thread:
+
// True if Init has been called.
bool initialized_;
diff --git a/chrome/browser/extensions/extension_messages_unittest.cc b/chrome/browser/extensions/extension_messages_unittest.cc
index 86a3180..27a5bd9 100644
--- a/chrome/browser/extensions/extension_messages_unittest.cc
+++ b/chrome/browser/extensions/extension_messages_unittest.cc
@@ -13,6 +13,7 @@ static void DispatchOnConnect(int source_port_id, 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
RendererExtensionBindings::Invoke(
ExtensionMessageService::kDispatchOnConnect, args);
}
diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc
index ac0bd97..555655c 100644
--- a/chrome/browser/extensions/extensions_service_unittest.cc
+++ b/chrome/browser/extensions/extensions_service_unittest.cc
@@ -572,7 +572,7 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectorySuccess) {
EXPECT_EQ(std::string(good2), loaded_[2]->id());
EXPECT_EQ(std::string("My extension 3"), loaded_[2]->name());
EXPECT_EQ(std::string(""), loaded_[2]->description());
- EXPECT_EQ(0u, loaded_[2]->content_scripts().size());
+ EXPECT_EQ(1u, loaded_[2]->content_scripts().size());
EXPECT_EQ(Extension::INTERNAL, loaded_[2]->location());
};
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index 5938bc1..5205bf6 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -1403,14 +1403,21 @@ IPC_BEGIN_MESSAGES(ViewHost)
std::string /* extension_id */,
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,
+ int /* routing_id */,
+ int /* tab_id */,
+ int /* port_id */)
+
// Send a message to an extension process. The handle is the value returned
- // by ViewHostMsg_OpenChannelToExtension.
+ // by ViewHostMsg_OpenChannelTo*.
IPC_MESSAGE_ROUTED2(ViewHostMsg_ExtensionPostMessage,
int /* port_id */,
std::string /* message */)
// Send a message to an extension process. The handle is the value returned
- // by ViewHostMsg_OpenChannelToExtension.
+ // by ViewHostMsg_OpenChannelTo*.
IPC_MESSAGE_CONTROL1(ViewHostMsg_ExtensionCloseChannel,
int /* port_id */)
diff --git a/chrome/renderer/extensions/bindings_utils.cc b/chrome/renderer/extensions/bindings_utils.cc
index fc9e98d..13738ca 100644
--- a/chrome/renderer/extensions/bindings_utils.cc
+++ b/chrome/renderer/extensions/bindings_utils.cc
@@ -44,34 +44,6 @@ v8::Handle<v8::Value> ExtensionBase::GetChromeHidden(
return hidden;
}
-v8::Handle<v8::Value> ExtensionBase::StartRequest(
- 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]->IsString() || !args[1]->IsInt32() ||
- !args[2]->IsBoolean())
- return v8::Undefined();
-
- std::string name = *v8::String::AsciiValue(args.Data());
- std::string json_args = *v8::String::Utf8Value(args[0]);
- int request_id = args[1]->Int32Value();
- bool has_callback = args[2]->BooleanValue();
-
- v8::Persistent<v8::Context> current_context =
- v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
- DCHECK(!current_context.IsEmpty());
- GetPendingRequestMap()[request_id].reset(new PendingRequest(
- current_context, *v8::String::AsciiValue(args.Data())));
-
- renderview->SendExtensionRequest(name, json_args, request_id, has_callback);
-
- return v8::Undefined();
-}
-
ContextList& GetContexts() {
return Singleton<SingletonData>::get()->contexts;
}
diff --git a/chrome/renderer/extensions/bindings_utils.h b/chrome/renderer/extensions/bindings_utils.h
index 1b21023..2e8935a5 100644
--- a/chrome/renderer/extensions/bindings_utils.h
+++ b/chrome/renderer/extensions/bindings_utils.h
@@ -39,10 +39,6 @@ class ExtensionBase : public v8::Extension {
// Returns a hidden variable for use by the bindings that is unreachable
// by the page.
static v8::Handle<v8::Value> GetChromeHidden(const v8::Arguments& args);
-
- // Starts an API request to the browser, with an optional callback. The
- // callback will be dispatched to EventBindings::HandleResponse.
- static v8::Handle<v8::Value> StartRequest(const v8::Arguments& args);
};
template<int kResourceId>
diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc
index e9efbad..03cccad 100644
--- a/chrome/renderer/extensions/event_bindings.cc
+++ b/chrome/renderer/extensions/event_bindings.cc
@@ -23,7 +23,6 @@ using bindings_utils::GetContexts;
using bindings_utils::GetStringResource;
using bindings_utils::ExtensionBase;
using bindings_utils::GetPendingRequestMap;
-using bindings_utils::PendingRequest;
using bindings_utils::PendingRequestMap;
namespace {
@@ -62,8 +61,6 @@ class ExtensionImpl : public ExtensionBase {
return v8::FunctionTemplate::New(AttachEvent);
} else if (name->Equals(v8::String::New("DetachEvent"))) {
return v8::FunctionTemplate::New(DetachEvent);
- } else if (name->Equals(v8::String::New("GetNextRequestId"))) {
- return v8::FunctionTemplate::New(GetNextRequestId);
}
return ExtensionBase::GetNativeFunction(name);
}
@@ -100,11 +97,6 @@ class ExtensionImpl : public ExtensionBase {
return v8::Undefined();
}
-
- static v8::Handle<v8::Value> GetNextRequestId(const v8::Arguments& args) {
- static int next_request_id = 0;
- return v8::Integer::New(next_request_id++);
- }
};
} // namespace
@@ -191,24 +183,3 @@ void EventBindings::CallFunction(const std::string& function_name,
CallFunctionInContext((*it)->context, function_name, argc, argv);
}
}
-
-// static
-void EventBindings::HandleResponse(int request_id, bool success,
- const std::string& response,
- const std::string& error) {
- PendingRequest* request = GetPendingRequestMap()[request_id].get();
- if (!request)
- return; // The frame went away.
-
- v8::HandleScope handle_scope;
- v8::Handle<v8::Value> argv[5];
- argv[0] = v8::Integer::New(request_id);
- argv[1] = v8::String::New(request->name.c_str());
- argv[2] = v8::Boolean::New(success);
- argv[3] = v8::String::New(response.c_str());
- argv[4] = v8::String::New(error.c_str());
- CallFunctionInContext(
- request->context, "handleResponse", arraysize(argv), argv);
-
- GetPendingRequestMap().erase(request_id);
-}
diff --git a/chrome/renderer/extensions/event_bindings.h b/chrome/renderer/extensions/event_bindings.h
index 285a379..ea0060f 100644
--- a/chrome/renderer/extensions/event_bindings.h
+++ b/chrome/renderer/extensions/event_bindings.h
@@ -31,11 +31,6 @@ class EventBindings {
// more details.
static void CallFunction(const std::string& function_name, int argc,
v8::Handle<v8::Value>* argv);
-
- // Handles a response to an API request.
- static void HandleResponse(int request_id, bool success,
- const std::string& response,
- const std::string& error);
};
#endif // CHROME_RENDERER_EXTENSIONS_EVENT_BINDINGS_H_
diff --git a/chrome/renderer/extensions/extension_api_client_unittest.cc b/chrome/renderer/extensions/extension_api_client_unittest.cc
index 6cef5750..663a892 100644
--- a/chrome/renderer/extensions/extension_api_client_unittest.cc
+++ b/chrome/renderer/extensions/extension_api_client_unittest.cc
@@ -7,7 +7,7 @@
#include "base/string_util.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/render_messages.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/test/render_view_test.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -91,7 +91,8 @@ TEST_F(ExtensionAPIClientTest, CallbackDispatching) {
ASSERT_GE(callback_id, 0);
// Now send the callback a response
- EventBindings::HandleResponse(callback_id, true, "{\"foo\":\"bar\"}", "");
+ ExtensionProcessBindings::HandleResponse(
+ callback_id, true, "{\"foo\":\"bar\"}", "");
// And verify that it worked
ASSERT_EQ("pass", GetConsoleMessage());
diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc
index 6b568a1..e68fb82 100644
--- a/chrome/renderer/extensions/extension_process_bindings.cc
+++ b/chrome/renderer/extensions/extension_process_bindings.cc
@@ -19,6 +19,8 @@ using bindings_utils::GetStringResource;
using bindings_utils::ContextInfo;
using bindings_utils::ContextList;
using bindings_utils::GetContexts;
+using bindings_utils::GetPendingRequestMap;
+using bindings_utils::PendingRequest;
using bindings_utils::ExtensionBase;
namespace {
@@ -58,8 +60,10 @@ class ExtensionImpl : public ExtensionBase {
if (name->Equals(v8::String::New("GetViews"))) {
return v8::FunctionTemplate::New(GetViews);
+ } else if (name->Equals(v8::String::New("GetNextRequestId"))) {
+ return v8::FunctionTemplate::New(GetNextRequestId);
} else if (names->find(*v8::String::AsciiValue(name)) != names->end()) {
- return v8::FunctionTemplate::New(ExtensionBase::StartRequest, name);
+ return v8::FunctionTemplate::New(StartRequest, name);
}
return ExtensionBase::GetNativeFunction(name);
@@ -88,6 +92,40 @@ class ExtensionImpl : public ExtensionBase {
}
return views;
}
+
+ static v8::Handle<v8::Value> GetNextRequestId(const v8::Arguments& args) {
+ static int next_request_id = 0;
+ return v8::Integer::New(next_request_id++);
+ }
+
+ // Starts an API request to the browser, with an optional callback. The
+ // callback will be dispatched to EventBindings::HandleResponse.
+ static v8::Handle<v8::Value> StartRequest(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]->IsString() || !args[1]->IsInt32() ||
+ !args[2]->IsBoolean())
+ return v8::Undefined();
+
+ std::string name = *v8::String::AsciiValue(args.Data());
+ std::string json_args = *v8::String::Utf8Value(args[0]);
+ int request_id = args[1]->Int32Value();
+ bool has_callback = args[2]->BooleanValue();
+
+ v8::Persistent<v8::Context> current_context =
+ v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
+ DCHECK(!current_context.IsEmpty());
+ GetPendingRequestMap()[request_id].reset(new PendingRequest(
+ current_context, *v8::String::AsciiValue(args.Data())));
+
+ renderview->SendExtensionRequest(name, json_args, request_id, has_callback);
+
+ return v8::Undefined();
+ }
};
} // namespace
@@ -100,3 +138,24 @@ void ExtensionProcessBindings::SetFunctionNames(
const std::vector<std::string>& names) {
ExtensionImpl::SetFunctionNames(names);
}
+
+// static
+void ExtensionProcessBindings::HandleResponse(int request_id, bool success,
+ const std::string& response,
+ const std::string& error) {
+ PendingRequest* request = GetPendingRequestMap()[request_id].get();
+ if (!request)
+ return; // The frame went away.
+
+ v8::HandleScope handle_scope;
+ v8::Handle<v8::Value> argv[5];
+ argv[0] = v8::Integer::New(request_id);
+ argv[1] = v8::String::New(request->name.c_str());
+ argv[2] = v8::Boolean::New(success);
+ argv[3] = v8::String::New(response.c_str());
+ argv[4] = v8::String::New(error.c_str());
+ bindings_utils::CallFunctionInContext(
+ request->context, "handleResponse", arraysize(argv), argv);
+
+ GetPendingRequestMap().erase(request_id);
+}
diff --git a/chrome/renderer/extensions/extension_process_bindings.h b/chrome/renderer/extensions/extension_process_bindings.h
index f703914..9e2c814 100644
--- a/chrome/renderer/extensions/extension_process_bindings.h
+++ b/chrome/renderer/extensions/extension_process_bindings.h
@@ -12,12 +12,15 @@
#include "v8/include/v8.h"
-class WebFrame;
-
class ExtensionProcessBindings {
public:
static void SetFunctionNames(const std::vector<std::string>& names);
static v8::Extension* Get();
+
+ // Handles a response to an API request.
+ static void HandleResponse(int request_id, bool success,
+ const std::string& response,
+ const std::string& error);
};
#endif // CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index cc69619..5f72843 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -37,6 +37,7 @@
#include "chrome/renderer/devtools_agent.h"
#include "chrome/renderer/devtools_client.h"
#include "chrome/renderer/extensions/event_bindings.h"
+#include "chrome/renderer/extensions/extension_process_bindings.h"
#include "chrome/renderer/localized_error.h"
#include "chrome/renderer/media/audio_renderer_impl.h"
#include "chrome/renderer/media/buffered_data_source.h"
@@ -2798,7 +2799,8 @@ void RenderView::OnExtensionResponse(int request_id,
bool success,
const std::string& response,
const std::string& error) {
- EventBindings::HandleResponse(request_id, success, response, error);
+ ExtensionProcessBindings::HandleResponse(
+ request_id, success, response, error);
}
// Dump all load time histograms.
diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd
index 241abb3..ed66407 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. mp6 -->
+without changes to the corresponding grd file. mp12 -->
<grit latest_public_release="0" current_release="1">
<outputs>
<output filename="grit/renderer_resources.h" type="rc_header">
diff --git a/chrome/renderer/resources/event_bindings.js b/chrome/renderer/resources/event_bindings.js
index a58cf37..5fb16f9 100644
--- a/chrome/renderer/resources/event_bindings.js
+++ b/chrome/renderer/resources/event_bindings.js
@@ -12,7 +12,6 @@ var chrome = chrome || {};
native function GetChromeHidden();
native function AttachEvent(eventName);
native function DetachEvent(eventName);
- native function GetNextRequestId();
var chromeHidden = GetChromeHidden();
@@ -149,45 +148,6 @@ var chrome = chrome || {};
delete attachedNamedEvents[this.eventName_];
};
- // Callback handling.
- var callbacks = [];
- chromeHidden.handleResponse = function(requestId, name,
- success, response, error) {
- try {
- if (!success) {
- if (!error)
- error = "Unknown error."
- console.error("Error during " + name + ": " + error);
- return;
- }
-
- if (callbacks[requestId]) {
- if (response) {
- callbacks[requestId](JSON.parse(response));
- } else {
- callbacks[requestId]();
- }
- }
- } finally {
- delete callbacks[requestId];
- }
- };
-
- // Send an API request and optionally register a callback.
- chromeHidden.sendRequest = function(request, args, callback) {
- // JSON.stringify doesn't support a root object which is undefined.
- if (args === undefined)
- args = null;
- var sargs = JSON.stringify(args);
- var requestId = GetNextRequestId();
- var hasCallback = false;
- if (callback) {
- hasCallback = true;
- callbacks[requestId] = callback;
- }
- request(sargs, requestId, hasCallback);
- }
-
// Special load events: we don't use the DOM unload because that slows
// down tab shutdown. On the other hand, onUnload might not always fire,
// since Chrome will terminate renderers on shutdown (SuddenTermination).
@@ -203,4 +163,8 @@ var chrome = chrome || {};
for (var i in allAttachedEvents)
allAttachedEvents[i].detach_();
}
+
+ chromeHidden.dispatchError = function(msg) {
+ console.error(msg);
+ }
})();
diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js
index 5b6239c..f3893fb 100644
--- a/chrome/renderer/resources/extension_process_bindings.js
+++ b/chrome/renderer/resources/extension_process_bindings.js
@@ -35,6 +35,7 @@ var chrome = chrome || {};
native function MoveBookmark();
native function SetBookmarkTitle();
native function GetChromeHidden();
+ native function GetNextRequestId();
if (!chrome)
chrome = {};
@@ -72,7 +73,44 @@ var chrome = chrome || {};
}
}
- var sendRequest = chromeHidden.sendRequest;
+ // Callback handling.
+ var callbacks = [];
+ chromeHidden.handleResponse = function(requestId, name,
+ success, response, error) {
+ try {
+ if (!success) {
+ if (!error)
+ error = "Unknown error."
+ console.error("Error during " + name + ": " + error);
+ return;
+ }
+
+ if (callbacks[requestId]) {
+ if (response) {
+ callbacks[requestId](JSON.parse(response));
+ } else {
+ callbacks[requestId]();
+ }
+ }
+ } finally {
+ delete callbacks[requestId];
+ }
+ };
+
+ // Send an API request and optionally register a callback.
+ function sendRequest(request, args, callback) {
+ // JSON.stringify doesn't support a root object which is undefined.
+ if (args === undefined)
+ args = null;
+ var sargs = JSON.stringify(args);
+ var requestId = GetNextRequestId();
+ var hasCallback = false;
+ if (callback) {
+ hasCallback = true;
+ callbacks[requestId] = callback;
+ }
+ request(sargs, requestId, hasCallback);
+ }
//----------------------------------------------------------------------------
@@ -492,6 +530,9 @@ var chrome = chrome || {};
chrome.self = chrome.self || {};
chromeHidden.onLoad.addListener(function (extensionId) {
+ 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);
});
diff --git a/chrome/renderer/resources/renderer_extension_bindings.js b/chrome/renderer/resources/renderer_extension_bindings.js
index b27a2df..2a41d5f 100644
--- a/chrome/renderer/resources/renderer_extension_bindings.js
+++ b/chrome/renderer/resources/renderer_extension_bindings.js
@@ -22,21 +22,26 @@ var chrome = chrome || {};
// Port object. Represents a connection to another script context through
// which messages can be passed.
chrome.Port = function(portId) {
- if (ports[portId]) {
- throw new Error("Port '" + portId + "' already exists.");
- }
- this.portId_ = portId; // TODO(mpcomplete): readonly
+ this.portId_ = portId;
this.onDisconnect = new chrome.Event();
this.onMessage = new chrome.Event();
- ports[portId] = this;
+ };
- var port = this;
+ chromeHidden.Port = {};
+
+ // 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) {
+ if (ports[portId]) {
+ throw new Error("Port '" + portId + "' already exists.");
+ }
+ var port = new chrome.Port(portId);
+ ports[portId] = port;
chromeHidden.onUnload.addListener(function() {
port.disconnect();
});
- };
-
- chromeHidden.Port = {};
+ return port;
+ }
// Called by native code when a channel has been opened to this context.
chromeHidden.Port.dispatchOnConnect = function(portId, tab, extensionId) {
@@ -44,9 +49,9 @@ var chrome = chrome || {};
// 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 || "");
+ var connectEvent = "channel-connect:" + extensionId;
if (chromeHidden.Event.hasListener(connectEvent)) {
- var port = new chrome.Port(portId);
+ var port = chromeHidden.Port.createPort(portId);
if (tab) {
tab = JSON.parse(tab);
}
@@ -93,6 +98,7 @@ var chrome = chrome || {};
// Extension object.
chrome.Extension = function(id) {
this.id_ = id;
+ this.onConnect = new chrome.Event('channel-connect:' + id);
};
// Opens a message channel to the extension. Returns a Port for
@@ -101,7 +107,7 @@ var chrome = chrome || {};
var portId = OpenChannelToExtension(this.id_);
if (portId == -1)
throw new Error("No such extension: '" + this.id_ + "'");
- return new chrome.Port(portId);
+ return chromeHidden.Port.createPort(portId);
};
// Returns a resource URL that can be used to fetch a resource from this
@@ -109,4 +115,6 @@ var chrome = chrome || {};
chrome.Extension.prototype.getURL = function(path) {
return "chrome-extension://" + this.id_ + "/" + path;
};
+
+ chrome.self = chrome.self || {};
})();
diff --git a/chrome/renderer/user_script_slave.cc b/chrome/renderer/user_script_slave.cc
index fa4eb7d..afd065f 100644
--- a/chrome/renderer/user_script_slave.cc
+++ b/chrome/renderer/user_script_slave.cc
@@ -26,8 +26,11 @@ static const char kUserScriptHead[] = "(function (unsafeWindow) {\n";
static const char kUserScriptTail[] = "\n})(window);";
// Creates a convenient reference to a content script's parent extension.
+// TODO(mpcomplete): self.onConnect is deprecated. Remove it at 1.0.
+// http://code.google.com/p/chromium/issues/detail?id=16356
static const char kInitExtension[] =
- "chrome.extension = new chrome.Extension('%s')";
+ "chrome.extension = new chrome.Extension('%s');"
+ "chrome.self.onConnect = chrome.extension.onConnect;";
UserScriptSlave::UserScriptSlave()
: shared_memory_(NULL),
diff --git a/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/background.html b/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/background.html
new file mode 100644
index 0000000..d68d4d6
--- /dev/null
+++ b/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/background.html
@@ -0,0 +1,38 @@
+<script>
+chrome.extension.onConnect.addListener(function(port) {
+ port.onMessage.addListener(function(msg) {
+ if (msg.testPostMessageFromTab) {
+ port.postMessage({success: true});
+ }
+ // Ignore other messages since they are from us.
+ });
+});
+
+// Tests that postMessage to the tab and its response works.
+function testPostMessage() {
+ var port = chrome.extension.connect();
+ port.postMessage({testPostMessage: true});
+ port.onMessage.addListener(function(msg) {
+ window.domAutomationController.send(msg.success);
+ port.disconnect();
+ });
+}
+
+// Tests that we get the disconnect event when the tab disconnect.
+function testDisconnect() {
+ var port = chrome.extension.connect();
+ port.postMessage({testDisconnect: true});
+ port.onDisconnect.addListener(function() {
+ window.domAutomationController.send(true);
+ });
+}
+
+// Tests that we get the disconnect event when the tab context closes.
+function testDisconnectOnClose() {
+ var port = chrome.extension.connect();
+ port.postMessage({testDisconnectOnClose: true});
+ port.onDisconnect.addListener(function() {
+ window.domAutomationController.send(true);
+ });
+}
+</script>
diff --git a/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/manifest.json b/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/manifest.json
index a7d4e32..b1a24a9 100644
--- a/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/manifest.json
+++ b/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/manifest.json
@@ -1,5 +1,12 @@
{
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRS2GUBOUAO5VZ2CMRId/eRR8/e9V42nUvY5XG+0sZ+JDHEjIQdq8qQy7HqdqEpCXKPMSPuMiC2t2HE9/hpL89SblNn3mwYPtSJGQdZvAzuv6SB0oA6jZ66V7+h/k0noGD3Tcu+Ko/vfkt5wCx2uHVK29k5JR/vGr0klaoVezGlwIDAQAB",
"version": "1.0",
- "name": "My extension 3"
+ "name": "My extension 3",
+ "background_page": "background.html",
+ "content_scripts": [
+ {
+ "matches": ["file://*"],
+ "js": ["page.js"]
+ }
+ ]
}
diff --git a/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/page.html b/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/page.html
new file mode 100644
index 0000000..ecaa68f
--- /dev/null
+++ b/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/page.html
@@ -0,0 +1 @@
+<script src="page.js"></script>
diff --git a/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/page.js b/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/page.js
new file mode 100644
index 0000000..7adb0c5
--- /dev/null
+++ b/chrome/test/data/extensions/good/Extensions/bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0/page.js
@@ -0,0 +1,31 @@
+var win = window;
+if (typeof(contentWindow) != 'undefined') {
+ win = contentWindow;
+}
+
+chrome.extension.onConnect.addListener(function(port) {
+ console.log('connected');
+ port.onMessage.addListener(function(msg) {
+ console.log('got ' + msg);
+ if (msg.testPostMessage) {
+ port.postMessage({success: true});
+ } else if (msg.testDisconnect) {
+ port.disconnect();
+ } else if (msg.testDisconnectOnClose) {
+ win.location = "about:blank";
+ }
+ // Ignore other messages since they are from us.
+ });
+});
+
+// Tests that postMessage to the extension and its response works.
+win.testPostMessageFromTab = function() {
+ var port = chrome.extension.connect();
+ port.postMessage({testPostMessageFromTab: true});
+ port.onMessage.addListener(function(msg) {
+ win.domAutomationController.send(msg.success);
+ console.log('sent ' + msg.success);
+ port.disconnect();
+ });
+ console.log('posted message');
+}
diff --git a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json
index dd4213b..a444d4b 100644
--- a/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json
+++ b/chrome/test/data/extensions/ui/create_extension_detail_value_expected_output/good-extension3.json
@@ -4,6 +4,11 @@
"name": "My extension 3",
"description": "",
"permissions": [],
- "content_scripts": [],
+ "content_scripts": [
+ {
+ "matches": ["file://*"],
+ "js": ["page.js"]
+ }
+ ],
"views": []
}