summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xchrome/browser/extensions/extension_message_service.cc41
-rwxr-xr-xchrome/browser/extensions/extension_message_service.h16
-rw-r--r--chrome/browser/extensions/extension_messages_unittest.cc1
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.cc19
-rw-r--r--chrome/browser/renderer_host/browser_render_process_host.h4
-rw-r--r--chrome/browser/renderer_host/render_process_host.h1
-rw-r--r--chrome/common/render_messages_internal.h8
-rwxr-xr-xchrome/renderer/extensions/event_bindings.cc50
-rwxr-xr-xchrome/renderer/extensions/event_bindings.h5
-rw-r--r--chrome/test/render_view_test.cc3
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();