diff options
-rwxr-xr-x | chrome/browser/extensions/extension_message_service.cc | 41 | ||||
-rwxr-xr-x | chrome/browser/extensions/extension_message_service.h | 16 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_messages_unittest.cc | 1 | ||||
-rw-r--r-- | chrome/browser/renderer_host/browser_render_process_host.cc | 19 | ||||
-rw-r--r-- | chrome/browser/renderer_host/browser_render_process_host.h | 4 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_process_host.h | 1 | ||||
-rw-r--r-- | chrome/common/render_messages_internal.h | 8 | ||||
-rwxr-xr-x | chrome/renderer/extensions/event_bindings.cc | 50 | ||||
-rwxr-xr-x | chrome/renderer/extensions/event_bindings.h | 5 | ||||
-rw-r--r-- | chrome/test/render_view_test.cc | 3 |
10 files changed, 137 insertions, 11 deletions
diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc index ed6fab8..4e9daba 100755 --- a/chrome/browser/extensions/extension_message_service.cc +++ b/chrome/browser/extensions/extension_message_service.cc @@ -64,12 +64,29 @@ ExtensionMessageService::ExtensionMessageService() void ExtensionMessageService::RegisterExtension( const std::string& extension_id, int render_process_id) { + DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); 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(MessageLoop::current()->type() == MessageLoop::TYPE_UI); + AutoLock lock(listener_lock_); + DCHECK(listeners_[event_name].count(render_process_id) == 0); + listeners_[event_name].insert(render_process_id); +} + +void ExtensionMessageService::RemoveEventListener(std::string event_name, + int render_process_id) { + DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); + AutoLock lock(listener_lock_); + DCHECK(listeners_[event_name].count(render_process_id) == 1); + listeners_[event_name].erase(render_process_id); +} + int ExtensionMessageService::OpenChannelToExtension( const std::string& extension_id, ResourceMessageFilter* source) { DCHECK(MessageLoop::current() == @@ -138,6 +155,13 @@ void ExtensionMessageService::PostMessageFromRenderer( void ExtensionMessageService::DispatchEventToRenderers( const std::string& event_name, const std::string& event_args) { + std::set<int> pids; + { + AutoLock lock(listener_lock_); + pids = listeners_[event_name]; + if (pids.empty()) + return; + } MessageLoop* io_thread = ChromeThread::GetMessageLoop(ChromeThread::IO); if (MessageLoop::current() != io_thread) { // Do the actual work on the IO thread. @@ -147,18 +171,19 @@ void ExtensionMessageService::DispatchEventToRenderers( return; } - // TODO(mpcomplete): we should only send messages to extension process - // renderers. - std::set<ResourceMessageFilter*>::iterator renderer; - for (renderer = renderers_unique_.begin(); - renderer != renderers_unique_.end(); ++renderer) { - (*renderer)->Send(new ViewMsg_ExtensionHandleEvent(event_name, event_args)); + // Send the event only to renderers that are listening for it. + for (std::set<int>::iterator pid = pids.begin(); pid != pids.end(); ++pid) { + RendererMap::iterator renderer = renderers_.find(*pid); + if (renderer == renderers_.end()) + continue; + ResourceMessageFilter* filter = renderer->second; + filter->Send(new ViewMsg_ExtensionHandleEvent(event_name, event_args)); } } void ExtensionMessageService::RendererReady(ResourceMessageFilter* renderer) { DCHECK(MessageLoop::current() == - ChromeThread::GetMessageLoop(ChromeThread::IO)); + ChromeThread::GetMessageLoop(ChromeThread::IO)); DCHECK(renderers_.find(renderer->GetProcessId()) == renderers_.end()); renderers_[renderer->GetProcessId()] = renderer; @@ -177,7 +202,7 @@ void ExtensionMessageService::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { DCHECK(MessageLoop::current() == - ChromeThread::GetMessageLoop(ChromeThread::IO)); + ChromeThread::GetMessageLoop(ChromeThread::IO)); DCHECK(type.value == NotificationType::RESOURCE_MESSAGE_FILTER_SHUTDOWN); ResourceMessageFilter* renderer = Source<ResourceMessageFilter>(source).ptr(); diff --git a/chrome/browser/extensions/extension_message_service.h b/chrome/browser/extensions/extension_message_service.h index 0c8d63f..52b1682 100755 --- a/chrome/browser/extensions/extension_message_service.h +++ b/chrome/browser/extensions/extension_message_service.h @@ -40,6 +40,10 @@ class ExtensionMessageService : public NotificationObserver { void RegisterExtension(const std::string& extension_id, int render_process_id); + // Add or remove |render_process_pid| as a listener for |event_name|. + void AddEventListener(std::string event_name, int render_process_id); + void RemoveEventListener(std::string event_name, int render_process_id); + // --- IO thread only: // Given an extension's ID, opens a channel between the given renderer "port" @@ -76,6 +80,16 @@ class ExtensionMessageService : public NotificationObserver { // 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_; + + // Protects listeners_ map, since it can be accessed from either the IO or + // UI thread. Be careful not to hold this lock when calling external code + // (especially sending messages) to avoid deadlock. + Lock listener_lock_; + // --- IO thread only: // The connection between two renderers. @@ -101,6 +115,8 @@ class ExtensionMessageService : public NotificationObserver { // Set to true when we start observing this notification. bool observing_renderer_shutdown_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionMessageService); }; #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_MESSAGE_SERVICE_H_ diff --git a/chrome/browser/extensions/extension_messages_unittest.cc b/chrome/browser/extensions/extension_messages_unittest.cc index 8f830dd..1ef9f43 100644 --- a/chrome/browser/extensions/extension_messages_unittest.cc +++ b/chrome/browser/extensions/extension_messages_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "chrome/common/render_messages.h" +#include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/extensions/renderer_extension_bindings.h" #include "chrome/test/render_view_test.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index af67872..f7e12c9 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -23,6 +23,7 @@ #include "base/string_util.h" #include "base/thread.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/history/history.h" #include "chrome/browser/plugin_service.h" @@ -588,6 +589,10 @@ void BrowserRenderProcessHost::OnMessageReceived(const IPC::Message& msg) { OnUpdatedCacheStats) IPC_MESSAGE_HANDLER(ViewHostMsg_SuddenTerminationChanged, SuddenTerminationChanged); + IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionAddListener, + OnExtensionAddListener) + IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionRemoveListener, + OnExtensionRemoveListener) IPC_MESSAGE_UNHANDLED_ERROR() IPC_END_MESSAGE_MAP_EX() @@ -797,3 +802,17 @@ void BrowserRenderProcessHost::Observe(NotificationType type, } } } + +void BrowserRenderProcessHost::OnExtensionAddListener( + const std::string& event_name) { + URLRequestContext* context = profile()->GetRequestContext(); + ExtensionMessageService* ems = ExtensionMessageService::GetInstance(context); + ems->AddEventListener(event_name, pid()); +} + +void BrowserRenderProcessHost::OnExtensionRemoveListener( + const std::string& event_name) { + URLRequestContext* context = profile()->GetRequestContext(); + ExtensionMessageService* ems = ExtensionMessageService::GetInstance(context); + ems->RemoveEventListener(event_name, pid()); +} diff --git a/chrome/browser/renderer_host/browser_render_process_host.h b/chrome/browser/renderer_host/browser_render_process_host.h index 1914432..b7e3631 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.h +++ b/chrome/browser/renderer_host/browser_render_process_host.h @@ -86,6 +86,10 @@ class BrowserRenderProcessHost : public RenderProcessHost, const NotificationSource& source, const NotificationDetails& details); + // An extension process started or stopped listening to an event. + void OnExtensionAddListener(const std::string& event_name); + void OnExtensionRemoveListener(const std::string& event_name); + private: // Control message handlers. void OnPageContents(const GURL& url, int32 page_id, diff --git a/chrome/browser/renderer_host/render_process_host.h b/chrome/browser/renderer_host/render_process_host.h index 0347427..44990d7 100644 --- a/chrome/browser/renderer_host/render_process_host.h +++ b/chrome/browser/renderer_host/render_process_host.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_RENDERER_HOST_RENDER_PROCESS_HOST_H_ #include <set> +#include <string> #include "base/id_map.h" #include "base/process.h" diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 41f7a6b..66f64b4 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -1296,6 +1296,14 @@ IPC_BEGIN_MESSAGES(ViewHost) std::string /* argument */, int /* callback id */) + // Notify the browser that this renderer added a listener to an event. + IPC_MESSAGE_CONTROL1(ViewHostMsg_ExtensionAddListener, + std::string /* name */) + + // Notify the browser that this renderer removed a listener from an event. + IPC_MESSAGE_CONTROL1(ViewHostMsg_ExtensionRemoveListener, + std::string /* name */) + #if defined(OS_MACOSX) // On OSX, we cannot allocated shared memory from within the sandbox, so // this call exists for the renderer to ask the browser to allocate memory diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc index 59ed319..06479dc 100755 --- a/chrome/renderer/extensions/event_bindings.cc +++ b/chrome/renderer/extensions/event_bindings.cc @@ -6,6 +6,7 @@ #include "base/basictypes.h" #include "base/singleton.h" +#include "chrome/common/render_messages.h" #include "chrome/renderer/extensions/bindings_utils.h" #include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/js_only_v8_extensions.h" @@ -14,15 +15,32 @@ namespace { +// Keep a local cache of RenderThread so that we can mock it out for unit tests. +static RenderThreadBase* render_thread = NULL; + +static RenderThreadBase* GetRenderThread() { + return render_thread ? render_thread : RenderThread::current(); +} + // Keep a list of contexts that have registered themselves with us. This lets // us know where to dispatch events when we receive them. typedef std::list< v8::Persistent<v8::Context> > ContextList; struct ExtensionData { ContextList contexts; + std::map<std::string, int> listener_count; }; ContextList& GetRegisteredContexts() { return Singleton<ExtensionData>::get()->contexts; } +int EventIncrementListenerCount(const std::string& event_name) { + ExtensionData *data = Singleton<ExtensionData>::get(); + return ++(data->listener_count[event_name]); +} +int EventDecrementListenerCount(const std::string& event_name) { + ExtensionData *data = Singleton<ExtensionData>::get(); + return --(data->listener_count[event_name]); +} + const char* kExtensionDeps[] = { JsonJsV8Extension::kName }; const char* kContextAttachCount = "chromium.attachCount"; @@ -47,10 +65,11 @@ class ExtensionImpl : public v8::Extension { } // Attach an event name to an object. - // TODO(mpcomplete): I'm just using this to register the v8 Context right now. - // The idea is to eventually notify the browser about what events are being - // listened to, so it can dispatch appropriately. static v8::Handle<v8::Value> AttachEvent(const v8::Arguments& args) { + DCHECK(args.Length() == 1); + // TODO(erikkay) should enforce that event name is a string in the bindings + DCHECK(args[0]->IsString() || args[0]->IsUndefined()); + v8::Persistent<v8::Context> context = v8::Persistent<v8::Context>::New(v8::Context::GetCurrent()); v8::Local<v8::Object> global = context->Global(); @@ -71,10 +90,22 @@ class ExtensionImpl : public v8::Extension { v8::String::New(kContextAttachCount), v8::Integer::New(account_count_value + 1)); + if (args[0]->IsString()) { + std::string event_name(*v8::String::AsciiValue(args[0])); + if (EventIncrementListenerCount(event_name) == 1) { + GetRenderThread()->Send( + new ViewHostMsg_ExtensionAddListener(event_name)); + } + } + return v8::Undefined(); } static v8::Handle<v8::Value> DetachEvent(const v8::Arguments& args) { + DCHECK(args.Length() == 1); + // TODO(erikkay) should enforce that event name is a string in the bindings + DCHECK(args[0]->IsString() || args[0]->IsUndefined()); + v8::Local<v8::Context> context = v8::Context::GetCurrent(); v8::Local<v8::Object> global = context->Global(); v8::Local<v8::Value> attach_count = global->GetHiddenValue( @@ -90,6 +121,14 @@ class ExtensionImpl : public v8::Extension { v8::String::New(kContextAttachCount), v8::Integer::New(account_count_value - 1)); + if (args[0]->IsString()) { + std::string event_name(*v8::String::AsciiValue(args[0])); + if (EventDecrementListenerCount(event_name) == 0) { + GetRenderThread()->Send( + new ViewHostMsg_ExtensionRemoveListener(event_name)); + } + } + return v8::Undefined(); } @@ -122,6 +161,11 @@ v8::Extension* EventBindings::Get() { return new ExtensionImpl(); } +// static +void EventBindings::SetRenderThread(RenderThreadBase* thread) { + render_thread = thread; +} + void EventBindings::CallFunction(const std::string& function_name, int argc, v8::Handle<v8::Value>* argv) { for (ContextList::iterator it = GetRegisteredContexts().begin(); diff --git a/chrome/renderer/extensions/event_bindings.h b/chrome/renderer/extensions/event_bindings.h index dd91811..0ca59c0 100755 --- a/chrome/renderer/extensions/event_bindings.h +++ b/chrome/renderer/extensions/event_bindings.h @@ -9,12 +9,17 @@ #include <string> +class RenderThreadBase; + // This class deals with the javascript bindings related to Event objects. class EventBindings { public: static const char* kName; // The v8::Extension name, for dependencies. static v8::Extension* Get(); + // Allow RenderThread to be mocked out. + static void SetRenderThread(RenderThreadBase* thread); + // Calls the given function in each registered context which is listening // for events. The function can be an object property, ie: // "chromium.Event.dispatch_". diff --git a/chrome/test/render_view_test.cc b/chrome/test/render_view_test.cc index 49d4c63..81d7347 100644 --- a/chrome/test/render_view_test.cc +++ b/chrome/test/render_view_test.cc @@ -59,6 +59,7 @@ void RenderViewTest::SetUp() { WebKit::registerExtension(EventBindings::Get()); WebKit::registerExtension(ExtensionProcessBindings::Get()); WebKit::registerExtension(RendererExtensionBindings::Get(&render_thread_)); + EventBindings::SetRenderThread(&render_thread_); // TODO(aa): Should some of this go to some other inheriting class? std::vector<std::string> names; @@ -80,6 +81,8 @@ void RenderViewTest::TearDown() { // Run the loop so the release task from the renderwidget executes. ProcessPendingMessages(); + EventBindings::SetRenderThread(NULL); + view_ = NULL; mock_process_.reset(); |