diff options
author | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-24 22:11:07 +0000 |
---|---|---|
committer | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-24 22:11:07 +0000 |
commit | 89ebc7edb982ebf5f8b185aa6e3aae6904eed965 (patch) | |
tree | ba32c69980b01c7c7d8d3f9d3cabebcb9ed88711 /chrome/browser/extensions | |
parent | 5101c0073bf723b6e6bc81273264eaf7ac124937 (diff) | |
download | chromium_src-89ebc7edb982ebf5f8b185aa6e3aae6904eed965.zip chromium_src-89ebc7edb982ebf5f8b185aa6e3aae6904eed965.tar.gz chromium_src-89ebc7edb982ebf5f8b185aa6e3aae6904eed965.tar.bz2 |
Exposes a chrome.devtools object to extensions. This allows extensions to call chrome.devtools.connect() to open up a Port by which it can receive devtools messages to implement the proposed perf trace extensions API documented here:
http://code.google.com/p/chromium/wiki/ExtensionsPerfTraceAPI
Review URL: http://codereview.chromium.org/159882
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24158 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/extensions')
11 files changed, 626 insertions, 5 deletions
diff --git a/chrome/browser/extensions/extension_devtools_bridge.cc b/chrome/browser/extensions/extension_devtools_bridge.cc new file mode 100644 index 0000000..2ac456f --- /dev/null +++ b/chrome/browser/extensions/extension_devtools_bridge.cc @@ -0,0 +1,104 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/extension_devtools_bridge.h" + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "chrome/browser/debugger/devtools_manager.h" +#include "chrome/browser/extensions/extension_devtools_events.h" +#include "chrome/browser/extensions/extension_devtools_manager.h" +#include "chrome/browser/extensions/extension_tabs_module.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/devtools_messages.h" + +ExtensionDevToolsBridge::ExtensionDevToolsBridge(int tab_id, + Profile* profile) + : tab_id_(tab_id), + inspected_rvh_(NULL), + profile_(profile), + on_page_event_name_( + ExtensionDevToolsEvents::OnPageEventNameForTab(tab_id)), + on_tab_url_change_event_name_( + ExtensionDevToolsEvents::OnTabUrlChangeEventNameForTab(tab_id)), + on_tab_close_event_name_( + ExtensionDevToolsEvents::OnTabCloseEventNameForTab(tab_id)) { + extension_devtools_manager_ = profile_->GetExtensionDevToolsManager(); + DCHECK(extension_devtools_manager_.get()); +} + +ExtensionDevToolsBridge::~ExtensionDevToolsBridge() { +} + +bool ExtensionDevToolsBridge::RegisterAsDevToolsClientHost() { + DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); + + Browser* browser; + TabStripModel* tab_strip; + TabContents* contents; + int tab_index; + if (ExtensionTabUtil::GetTabById(tab_id_, profile_, &browser, &tab_strip, + &contents, &tab_index)) { + inspected_rvh_ = contents->render_view_host(); + DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( + inspected_rvh_, this); + return true; + } + return false; +} + +void ExtensionDevToolsBridge::UnregisterAsDevToolsClientHost() { + DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); + + if (inspected_rvh_) { + DevToolsManager::GetInstance()->UnregisterDevToolsClientHostFor( + inspected_rvh_); + inspected_rvh_ = NULL; + } +} + +// If the tab we are looking at is going away then we fire a closing event at +// the extension. +void ExtensionDevToolsBridge::InspectedTabClosing() { + DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); + + std::string json("[{}]"); + profile_->GetExtensionMessageService()-> + DispatchEventToRenderers(on_tab_close_event_name_, json); + + // This may result in this object being destroyed. + extension_devtools_manager_->BridgeClosingForTab(tab_id_); +} + +void ExtensionDevToolsBridge::SendMessageToClient(const IPC::Message& msg) { + IPC_BEGIN_MESSAGE_MAP(ExtensionDevToolsBridge, msg) + IPC_MESSAGE_HANDLER(DevToolsClientMsg_RpcMessage, OnRpcMessage); + IPC_MESSAGE_UNHANDLED_ERROR() + IPC_END_MESSAGE_MAP() +} + +static const char kTimelineAgentClassName[] = "TimelineAgentClass"; +static const char kPageEventMessageName[] = "PageEventMessage"; +static const char kTabUrlChangeEventMessageName[] = "TabUrlChangeEventMessage"; + +void ExtensionDevToolsBridge::OnRpcMessage(const std::string& class_name, + const std::string& message_name, + const std::string& msg) { + DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); + // TODO(jamesr): Update the filtering and message creation logic once + // the TimelineAgent lands in WebKit. + if (class_name == kTimelineAgentClassName) { + if (message_name == kPageEventMessageName) { + std::string json = StringPrintf("[{\"record\": \"%s\"}]", msg.c_str()); + profile_->GetExtensionMessageService()-> + DispatchEventToRenderers(on_page_event_name_, json); + } else if (message_name == kTabUrlChangeEventMessageName) { + std::string json = StringPrintf("[{\"record\": \"%s\"}]", msg.c_str()); + profile_->GetExtensionMessageService()-> + DispatchEventToRenderers(on_tab_url_change_event_name_, json); + } + } +} + diff --git a/chrome/browser/extensions/extension_devtools_bridge.h b/chrome/browser/extensions/extension_devtools_bridge.h new file mode 100644 index 0000000..b4cb98d --- /dev/null +++ b/chrome/browser/extensions/extension_devtools_bridge.h @@ -0,0 +1,60 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_DEVTOOLS_BRIDGE_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_DEVTOOLS_BRIDGE_H_ + +#include <string> + +#include "base/ref_counted.h" +#include "chrome/browser/debugger/devtools_client_host.h" +#include "chrome/browser/extensions/extension_devtools_manager.h" +#include "chrome/browser/extensions/extension_message_service.h" + +class Profile; +class RenderViewHost; + +// This class is a DevToolsClientHost that fires extension events. +class ExtensionDevToolsBridge : public DevToolsClientHost { + public: + ExtensionDevToolsBridge(int tab_id, Profile* profile); + virtual ~ExtensionDevToolsBridge(); + + bool RegisterAsDevToolsClientHost(); + void UnregisterAsDevToolsClientHost(); + + // DevToolsClientHost, called when the tab inspected by this client is + // closing. + virtual void InspectedTabClosing(); + + // DevToolsClientHost, called to send a message to this host. + virtual void SendMessageToClient(const IPC::Message& msg); + + private: + void OnRpcMessage(const std::string& class_name, + const std::string& message_name, + const std::string& msg); + + // ID of the tab we are monitoring. + int tab_id_; + // Host of the tab we are monitoring, NULL if not monitoring anything. + RenderViewHost* inspected_rvh_; + + scoped_refptr<ExtensionDevToolsManager> extension_devtools_manager_; + scoped_refptr<ExtensionMessageService> extension_message_service_; + + // Profile that owns our tab + Profile* profile_; + + // The names of the events fired at extensions depend on the tab id, + // so we store the various event names in each bridge. + const std::string on_page_event_name_; + const std::string on_tab_url_change_event_name_; + const std::string on_tab_close_event_name_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsBridge); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_DEVTOOLS_BRIDGE_H_ + diff --git a/chrome/browser/extensions/extension_devtools_browsertest.cc b/chrome/browser/extensions/extension_devtools_browsertest.cc new file mode 100644 index 0000000..85d34ac --- /dev/null +++ b/chrome/browser/extensions/extension_devtools_browsertest.cc @@ -0,0 +1,15 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/extension_devtools_browsertest.h" + +#include "chrome/common/chrome_switches.h" + +void ExtensionDevToolsBrowserTest::SetUpCommandLine(CommandLine* command_line) { + ExtensionBrowserTest::SetUpCommandLine(command_line); + + command_line->AppendSwitch(switches::kEnableExtensionTimelineApi); +} + + diff --git a/chrome/browser/extensions/extension_devtools_browsertest.h b/chrome/browser/extensions/extension_devtools_browsertest.h new file mode 100644 index 0000000..7fa6435 --- /dev/null +++ b/chrome/browser/extensions/extension_devtools_browsertest.h @@ -0,0 +1,22 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_DEVTOOLS_BROWSERTEST_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_DEVTOOLS_BROWSERTEST_H_ + +#include "base/command_line.h" +#include "chrome/browser/extensions/extension_browsertest.h" + +// Subclass of ExtensionBrowserTest that enables the devtools +// command line features. +class ExtensionDevToolsBrowserTest : public ExtensionBrowserTest { + protected: + virtual void SetUpCommandLine(CommandLine* command_line); + + private: + + NotificationRegistrar registrar_; +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_DEVTOOLS_BROWSERTEST_H_ diff --git a/chrome/browser/extensions/extension_devtools_browsertests.cc b/chrome/browser/extensions/extension_devtools_browsertests.cc new file mode 100644 index 0000000..9aaa071 --- /dev/null +++ b/chrome/browser/extensions/extension_devtools_browsertests.cc @@ -0,0 +1,170 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/ref_counted.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/debugger/devtools_manager.h" +#include "chrome/browser/debugger/devtools_client_host.h" +#include "chrome/browser/extensions/extension_devtools_browsertest.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/extensions/extensions_service.h" +#include "chrome/browser/extensions/extension_tabs_module.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/site_instance.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/views/extensions/extension_shelf.h" +#include "chrome/browser/views/frame/browser_view.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/devtools_messages.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); + EXPECT_TRUE(host); + return host; +} + +// Tests for the experimental timeline extensions API. +IN_PROC_BROWSER_TEST_F(ExtensionDevToolsBrowserTest, TimelineApi) { + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("devtools").AppendASCII("timeline_api"))); + + // Get the ExtensionHost that is hosting our background page. + ExtensionProcessManager* manager = + browser()->profile()->GetExtensionProcessManager(); + ExtensionHost* host = FindHostWithPath(manager, "/background.html", 1); + + // Grab a handle to the DevToolsManager so we can forward messages to it. + DevToolsManager* devtools_manager = DevToolsManager::GetInstance(); + + // Grab the tab_id of whatever tab happens to be first. + TabContents* tab_contents = browser()->tabstrip_model()->GetTabContentsAt(0); + ASSERT_TRUE(tab_contents); + int tab_id = ExtensionTabUtil::GetTabId(tab_contents); + + // Test setup. + bool result = false; + std::wstring register_listeners_js = StringPrintf(L"setListenersOnTab(%d)", + tab_id); + ui_test_utils::ExecuteJavaScriptAndExtractBool( + host->render_view_host(), L"", register_listeners_js, &result); + EXPECT_TRUE(result); + + // Setting the events should have caused an ExtensionDevToolsBridge to be + // registered for the tab's RenderViewHost. + DevToolsClientHost* devtools_client_host = + devtools_manager->GetDevToolsClientHostFor( + tab_contents->render_view_host()); + ASSERT_TRUE(devtools_client_host); + + // Test onTabUrlChange event. + DevToolsClientMsg_RpcMessage tabUrlChangeEventMessage( + "TimelineAgentClass", "TabUrlChangeEventMessage", "{}"); + devtools_client_host->SendMessageToClient(tabUrlChangeEventMessage); + ui_test_utils::ExecuteJavaScriptAndExtractBool( + host->render_view_host(), + L"", + L"testReceiveTabUrlChangeEvent()", + &result); + EXPECT_TRUE(result); + + // Test onPageEvent event. + result = false; + DevToolsClientMsg_RpcMessage pageEventMessage( + "TimelineAgentClass", "PageEventMessage", "{}"); + devtools_client_host->SendMessageToClient(pageEventMessage); + ui_test_utils::ExecuteJavaScriptAndExtractBool( + host->render_view_host(), L"", L"testReceivePageEvent()", &result); + EXPECT_TRUE(result); + + // Test onTabClose event. + result = false; + devtools_manager->UnregisterDevToolsClientHostFor( + tab_contents->render_view_host()); + ui_test_utils::ExecuteJavaScriptAndExtractBool( + host->render_view_host(), L"", L"testReceiveTabCloseEvent()", &result); + EXPECT_TRUE(result); +} + + +// Tests that ref counting of listeners from multiple processes works. +IN_PROC_BROWSER_TEST_F(ExtensionDevToolsBrowserTest, ProcessRefCounting) { + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("devtools").AppendASCII("timeline_api"))); + + // Get the ExtensionHost that is hosting our background page. + ExtensionProcessManager* manager = + browser()->profile()->GetExtensionProcessManager(); + ExtensionHost* host_one = FindHostWithPath(manager, "/background.html", 1); + + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("devtools").AppendASCII("timeline_api_two"))); + ExtensionHost* host_two = FindHostWithPath(manager, + "/background_two.html", 2); + + DevToolsManager* devtools_manager = DevToolsManager::GetInstance(); + + // Grab the tab_id of whatever tab happens to be first. + TabContents* tab_contents = browser()->tabstrip_model()->GetTabContentsAt(0); + ASSERT_TRUE(tab_contents); + int tab_id = ExtensionTabUtil::GetTabId(tab_contents); + + // Test setup. + bool result = false; + std::wstring register_listeners_js = StringPrintf(L"setListenersOnTab(%d)", + tab_id); + ui_test_utils::ExecuteJavaScriptAndExtractBool( + host_one->render_view_host(), L"", register_listeners_js, &result); + EXPECT_TRUE(result); + + // Setting the event listeners should have caused an ExtensionDevToolsBridge + // to be registered for the tab's RenderViewHost. + ASSERT_TRUE(devtools_manager->GetDevToolsClientHostFor( + tab_contents->render_view_host())); + + // Register listeners from the second extension as well. + std::wstring script = StringPrintf(L"registerListenersForTab(%d)", tab_id); + ui_test_utils::ExecuteJavaScriptAndExtractBool( + host_two->render_view_host(), L"", script, &result); + EXPECT_TRUE(result); + + // Removing the listeners from the first extension should leave the bridge + // alive. + result = false; + ui_test_utils::ExecuteJavaScriptAndExtractBool( + host_one->render_view_host(), L"", L"unregisterListeners()", &result); + EXPECT_TRUE(result); + ASSERT_TRUE(devtools_manager->GetDevToolsClientHostFor( + tab_contents->render_view_host())); + + // Removing the listeners from the second extension should tear the bridge + // down. + result = false; + ui_test_utils::ExecuteJavaScriptAndExtractBool( + host_two->render_view_host(), L"", L"unregisterListeners()", &result); + EXPECT_TRUE(result); + ASSERT_FALSE(devtools_manager->GetDevToolsClientHostFor( + tab_contents->render_view_host())); +} diff --git a/chrome/browser/extensions/extension_devtools_events.cc b/chrome/browser/extensions/extension_devtools_events.cc new file mode 100644 index 0000000..979726c --- /dev/null +++ b/chrome/browser/extensions/extension_devtools_events.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/extension_devtools_events.h" + +#include <vector> + +#include "base/string_util.h" + +// These string constants and the formats used in this file must stay +// in sync with chrome/renderer/resources/extension_process_bindings.js +static const char kDevToolsEventPrefix[] = "devtools."; +static const char kOnPageEventName[] = "onPageEvent"; +static const char kOnTabUrlChangeEventName[] = "onTabUrlChange"; +static const char kOnTabCloseEventName[] = "onTabClose"; + +// static +bool ExtensionDevToolsEvents::IsDevToolsEventName( + const std::string& event_name, int* tab_id) { + // We only care about events of the form "devtools.34.*", where 34 is + // a tab id. + if (IsStringASCII(event_name) && + StartsWithASCII(event_name, + kDevToolsEventPrefix, + true /* case_sensitive */)) { + // At this point we want something like "4.onPageEvent" + std::vector<std::string> parts; + SplitString(event_name.substr(strlen(kDevToolsEventPrefix)), '.', &parts); + if (parts.size() == 2 && StringToInt(parts[0], tab_id)) { + return true; + } + } + return false; +} + +// static +std::string ExtensionDevToolsEvents::OnPageEventNameForTab(int tab_id) { + return StringPrintf("%s%d.%s", + kDevToolsEventPrefix, + tab_id, + kOnPageEventName); +} + +// static +std::string ExtensionDevToolsEvents::OnTabUrlChangeEventNameForTab(int tab_id) { + return StringPrintf("%s%d.%s", + kDevToolsEventPrefix, + tab_id, + kOnTabUrlChangeEventName); +} + +// static +std::string ExtensionDevToolsEvents::OnTabCloseEventNameForTab(int tab_id) { + return StringPrintf("%s%d.%s", + kDevToolsEventPrefix, + tab_id, + kOnTabCloseEventName); +} + diff --git a/chrome/browser/extensions/extension_devtools_events.h b/chrome/browser/extensions/extension_devtools_events.h new file mode 100644 index 0000000..ff985b2 --- /dev/null +++ b/chrome/browser/extensions/extension_devtools_events.h @@ -0,0 +1,37 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_DEVTOOLS_EVENTS_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_DEVTOOLS_EVENTS_H_ + +#include <string> + +#include "base/basictypes.h" + +// Static utility functions for dealing with extension devtools event names. +// The format of the event names is <prefix>.<tab id>.<event name> +// Equivalent name munging is done in the extension process in JavaScript +// by chrome/renderer/resources/extension_process_bindings.js +class ExtensionDevToolsEvents { + public: + // Checks if an event name is a magic devtools event name. If so, + // the tab id of the event is put in *tab_id. + static bool IsDevToolsEventName(const std::string& event_name, int* tab_id); + + // Generates the event string for an onPageEvent for a given tab. + static std::string OnPageEventNameForTab(int tab_id); + + // Generates the event string for an onPageEvent for a given tab. + static std::string OnTabUrlChangeEventNameForTab(int tab_id); + + // Generates the event string for an onTabCloseEvent for a given tab. + static std::string OnTabCloseEventNameForTab(int tab_id); + + private: + + DISALLOW_IMPLICIT_CONSTRUCTORS(ExtensionDevToolsEvents); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_DEVTOOLS_EVENTS_H_ + diff --git a/chrome/browser/extensions/extension_devtools_manager.cc b/chrome/browser/extensions/extension_devtools_manager.cc new file mode 100644 index 0000000..20f4eb0 --- /dev/null +++ b/chrome/browser/extensions/extension_devtools_manager.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/extension_devtools_manager.h" + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "base/task.h" +#include "chrome/browser/extensions/extension_devtools_bridge.h" +#include "chrome/browser/extensions/extension_devtools_events.h" + +ExtensionDevToolsManager::ExtensionDevToolsManager(Profile* profile) + : profile_(profile), + ui_loop_(NULL) { + DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); + ui_loop_ = MessageLoop::current(); +} + +ExtensionDevToolsManager::~ExtensionDevToolsManager() { +} + +void ExtensionDevToolsManager::AddEventListener(const std::string& event_name, + int render_process_id) { + int tab_id; + if (ExtensionDevToolsEvents::IsDevToolsEventName(event_name, &tab_id)) { + // Add the renderer process ID to the set of processes interested + // in this tab. + tab_id_to_render_process_ids_[tab_id].insert(render_process_id); + if (tab_id_to_bridge_.count(tab_id) == 0) { + // Create a new bridge for this tab if there isn't one already. + linked_ptr<ExtensionDevToolsBridge> bridge( + new ExtensionDevToolsBridge(tab_id, profile_)); + if (bridge->RegisterAsDevToolsClientHost()) { + tab_id_to_bridge_[tab_id] = bridge; + } + } + } +} + +void ExtensionDevToolsManager::RemoveEventListener( + const std::string& event_name, + int render_process_id) { + int tab_id; + if (ExtensionDevToolsEvents::IsDevToolsEventName(event_name, &tab_id)) { + std::map<int, std::set<int> >::iterator it = + tab_id_to_render_process_ids_.find(tab_id); + if (it != tab_id_to_render_process_ids_.end()) { + // Remove the process from the set of processes interested in this tab. + it->second.erase(render_process_id); + if (it->second.empty()) { + // No renderers have registered listeners for this tab, so kill the + // bridge if there is one. + if (tab_id_to_bridge_.count(tab_id) != 0) { + linked_ptr<ExtensionDevToolsBridge> bridge(tab_id_to_bridge_[tab_id]); + bridge->UnregisterAsDevToolsClientHost(); + tab_id_to_bridge_.erase(tab_id); + } + } + } + } +} + +void ExtensionDevToolsManager::BridgeClosingForTab(int tab_id) { + if (tab_id_to_bridge_.count(tab_id) != 0) { + linked_ptr<ExtensionDevToolsBridge> bridge(tab_id_to_bridge_[tab_id]); + bridge->UnregisterAsDevToolsClientHost(); + tab_id_to_bridge_.erase(tab_id); + } + tab_id_to_render_process_ids_.erase(tab_id); +} + diff --git a/chrome/browser/extensions/extension_devtools_manager.h b/chrome/browser/extensions/extension_devtools_manager.h new file mode 100644 index 0000000..35e7cec --- /dev/null +++ b/chrome/browser/extensions/extension_devtools_manager.h @@ -0,0 +1,63 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_DEVTOOLS_MANAGER_H_ +#define CHROME_BROWSER_EXTENSIONS_EXTENSION_DEVTOOLS_MANAGER_H_ + +#include <map> +#include <set> +#include <string> +#include "base/linked_ptr.h" +#include "base/ref_counted.h" + +class ExtensionDevToolsBridge; +class MessageLoop; +class Profile; + +// This class manages the lifetimes of ExtensionDevToolsBridge objects. +// The manager is owned by the Profile. +// +// The lifetime of an ExtensionDevToolsBridge object is determined by: +// * the existence of registered event handlers for the bridge's tab +// * the lifetime of the inspected tab +// +// The manager is alerted whenever an event listener is added or removed and +// keeps track of the set of renderers with event listeners registered for each +// tab. A new bridge object is created for a tab when the first event listener +// is registered on that tab. A bridge object is destroyed when all event +// listeners are removed, the inspected tab closes, or when the manager itself +// is destroyed. + +class ExtensionDevToolsManager + : public base::RefCountedThreadSafe<ExtensionDevToolsManager> { + public: + // UI thread only: + explicit ExtensionDevToolsManager(Profile* profile); + ~ExtensionDevToolsManager(); + + void AddEventListener(const std::string& event_name, + int render_process_id); + + void RemoveEventListener(const std::string& event_name, + int render_process_id); + + void BridgeClosingForTab(int tab_id); + + private: + + // Map of tab IDs to the ExtensionDevToolsBridge connected to the tab + std::map<int, linked_ptr<ExtensionDevToolsBridge> > tab_id_to_bridge_; + + // Map of tab IDs to the set of render_process_ids that have registered + // event handlers for the tab. + std::map<int, std::set<int> > tab_id_to_render_process_ids_; + + Profile* profile_; + MessageLoop* ui_loop_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionDevToolsManager); +}; + +#endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_DEVTOOLS_MANAGER_H_ + diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc index a4b1fd6d..edc2cf9 100644 --- a/chrome/browser/extensions/extension_message_service.cc +++ b/chrome/browser/extensions/extension_message_service.cc @@ -105,7 +105,10 @@ const char ExtensionMessageService::kDispatchEvent[] = "Event.dispatchJSON"; ExtensionMessageService::ExtensionMessageService(Profile* profile) - : ui_loop_(MessageLoop::current()), profile_(profile), next_port_id_(0) { + : ui_loop_(MessageLoop::current()), + profile_(profile), + extension_devtools_manager_(NULL), + next_port_id_(0) { DCHECK_EQ(ui_loop_->type(), MessageLoop::TYPE_UI); registrar_.Add(this, NotificationType::RENDERER_PROCESS_TERMINATED, @@ -114,6 +117,8 @@ ExtensionMessageService::ExtensionMessageService(Profile* profile) NotificationService::AllSources()); registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_DELETED, NotificationService::AllSources()); + + extension_devtools_manager_ = profile_->GetExtensionDevToolsManager(); } ExtensionMessageService::~ExtensionMessageService() { @@ -136,6 +141,11 @@ void ExtensionMessageService::AddEventListener(const std::string& event_name, DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); DCHECK_EQ(listeners_[event_name].count(render_process_id), 0u) << event_name; listeners_[event_name].insert(render_process_id); + + if (extension_devtools_manager_.get()) { + extension_devtools_manager_->AddEventListener(event_name, + render_process_id); + } } void ExtensionMessageService::RemoveEventListener(const std::string& event_name, @@ -149,6 +159,11 @@ void ExtensionMessageService::RemoveEventListener(const std::string& event_name, DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); DCHECK_EQ(listeners_[event_name].count(render_process_id), 1u) << event_name; listeners_[event_name].erase(render_process_id); + + if (extension_devtools_manager_.get()) { + extension_devtools_manager_->RemoveEventListener(event_name, + render_process_id); + } } void ExtensionMessageService::AllocatePortIdPair(int* port1, int* port2) { @@ -345,6 +360,7 @@ void ExtensionMessageService::CloseChannelImpl( channels_.erase(channel_iter); } + void ExtensionMessageService::PostMessageFromRenderer( int source_port_id, const std::string& message) { DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); @@ -394,13 +410,12 @@ void ExtensionMessageService::Observe(NotificationType type, RenderProcessHost* renderer = Source<RenderProcessHost>(source).ptr(); OnSenderClosed(renderer); - // Remove this renderer from our listener maps. + // Remove all event listeners associated with this renderer 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); + if (current->second.count(renderer->pid()) != 0) + RemoveEventListener(current->first, renderer->pid()); } break; } diff --git a/chrome/browser/extensions/extension_message_service.h b/chrome/browser/extensions/extension_message_service.h index a6b1cd5..32ed945 100644 --- a/chrome/browser/extensions/extension_message_service.h +++ b/chrome/browser/extensions/extension_message_service.h @@ -13,6 +13,7 @@ #include "base/lock.h" #include "base/ref_counted.h" #include "chrome/common/notification_registrar.h" +#include "chrome/browser/extensions/extension_devtools_manager.h" #include "ipc/ipc_message.h" class MessageLoop; @@ -157,6 +158,8 @@ class ExtensionMessageService MessageChannelMap channels_; + scoped_refptr<ExtensionDevToolsManager> extension_devtools_manager_; + // 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; |