diff options
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": [] } |