Index: chrome/renderer/extensions/event_bindings.cc |
diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc |
index 94a0203ff8d2746daa98573b49e7b80a2b12f551..06d9dbcbabdce873c423cc41a1865bfa89f5dc13 100644 |
--- a/chrome/renderer/extensions/event_bindings.cc |
+++ b/chrome/renderer/extensions/event_bindings.cc |
@@ -6,11 +6,15 @@ |
#include <vector> |
+#include "base/bind.h" |
#include "base/basictypes.h" |
#include "base/lazy_instance.h" |
+#include "base/memory/scoped_ptr.h" |
#include "base/message_loop.h" |
#include "chrome/common/extensions/extension_messages.h" |
#include "chrome/common/extensions/extension_set.h" |
+#include "chrome/common/extensions/event_filter.h" |
+#include "chrome/common/extensions/value_counter.h" |
#include "chrome/common/url_constants.h" |
#include "chrome/common/view_type.h" |
#include "chrome/renderer/extensions/chrome_v8_context.h" |
@@ -21,6 +25,7 @@ |
#include "chrome/renderer/extensions/extension_helper.h" |
#include "chrome/renderer/extensions/user_script_slave.h" |
#include "content/public/renderer/render_thread.h" |
+#include "content/public/renderer/v8_value_converter.h" |
#include "googleurl/src/gurl.h" |
#include "grit/renderer_resources.h" |
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" |
@@ -48,33 +53,57 @@ typedef std::map<std::string, int> EventListenerCounts; |
base::LazyInstance<std::map<std::string, EventListenerCounts> > |
g_listener_counts = LAZY_INSTANCE_INITIALIZER; |
+// A map of event names to a (filter -> count) map. The map is used to keep |
+// track of which filters are in effect for which events. |
+// We notify the browser about filtered event listeners when we transition |
+// between 0 and 1. |
+typedef std::map<std::string, linked_ptr<extensions::ValueCounter> > |
+ FilteredEventListenerCounts; |
+ |
+// A map of extension IDs to filtered listener counts for that extension. |
+base::LazyInstance<std::map<std::string, FilteredEventListenerCounts> > |
+ g_filtered_listener_counts = LAZY_INSTANCE_INITIALIZER; |
+ |
// TODO(koz): Merge this into EventBindings. |
class ExtensionImpl : public ChromeV8Extension { |
public: |
- explicit ExtensionImpl(ExtensionDispatcher* dispatcher) |
- : ChromeV8Extension(dispatcher) { |
- RouteStaticFunction("AttachEvent", &AttachEvent); |
- RouteStaticFunction("DetachEvent", &DetachEvent); |
+ ExtensionImpl(ExtensionDispatcher* dispatcher, |
+ extensions::EventFilter* event_filter) |
+ : ChromeV8Extension(dispatcher), |
+ event_filter_(event_filter) { |
+ RouteFunction("AttachEvent", |
+ base::Bind(&ExtensionImpl::AttachEvent, |
+ base::Unretained(this))); |
+ RouteFunction("DetachEvent", |
+ base::Bind(&ExtensionImpl::DetachEvent, |
+ base::Unretained(this))); |
+ RouteFunction("AttachFilteredEvent", |
+ base::Bind(&ExtensionImpl::AttachFilteredEvent, |
+ base::Unretained(this))); |
+ RouteFunction("DetachFilteredEvent", |
+ base::Bind(&ExtensionImpl::DetachFilteredEvent, |
+ base::Unretained(this))); |
+ RouteFunction("MatchAgainstEventFilter", |
+ base::Bind(&ExtensionImpl::MatchAgainstEventFilter, |
+ base::Unretained(this))); |
} |
~ExtensionImpl() {} |
// Attach an event name to an object. |
- static v8::Handle<v8::Value> AttachEvent(const v8::Arguments& args) { |
+ 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()); |
if (args[0]->IsString()) { |
- ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); |
+ std::string event_name = *v8::String::AsciiValue(args[0]->ToString()); |
const ChromeV8ContextSet& context_set = |
- self->extension_dispatcher()->v8_context_set(); |
+ extension_dispatcher()->v8_context_set(); |
ChromeV8Context* context = context_set.GetCurrent(); |
CHECK(context); |
- std::string event_name(*v8::String::AsciiValue(args[0])); |
- ExtensionDispatcher* extension_dispatcher = self->extension_dispatcher(); |
- if (!extension_dispatcher->CheckCurrentContextAccessToExtensionAPI( |
+ if (!extension_dispatcher()->CheckCurrentContextAccessToExtensionAPI( |
event_name)) |
return v8::Undefined(); |
@@ -89,24 +118,25 @@ class ExtensionImpl : public ChromeV8Extension { |
// This is called the first time the page has added a listener. Since |
// the background page is the only lazy page, we know this is the first |
// time this listener has been registered. |
- if (self->IsLazyBackgroundPage(context->extension())) { |
+ if (IsLazyBackgroundPage(context->extension())) { |
content::RenderThread::Get()->Send( |
new ExtensionHostMsg_AddLazyListener(extension_id, event_name)); |
} |
} |
- |
return v8::Undefined(); |
} |
- static v8::Handle<v8::Value> DetachEvent(const v8::Arguments& args) { |
+ v8::Handle<v8::Value> DetachEvent(const v8::Arguments& args) { |
DCHECK(args.Length() == 2); |
// TODO(erikkay) should enforce that event name is a string in the bindings |
DCHECK(args[0]->IsString() || args[0]->IsUndefined()); |
if (args[0]->IsString() && args[1]->IsBoolean()) { |
- ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); |
+ std::string event_name = *v8::String::AsciiValue(args[0]->ToString()); |
+ bool is_manual = args[1]->BooleanValue(); |
+ |
const ChromeV8ContextSet& context_set = |
- self->extension_dispatcher()->v8_context_set(); |
+ extension_dispatcher()->v8_context_set(); |
ChromeV8Context* context = context_set.GetCurrent(); |
if (!context) |
return v8::Undefined(); |
@@ -114,8 +144,6 @@ class ExtensionImpl : public ChromeV8Extension { |
std::string extension_id = context->GetExtensionID(); |
EventListenerCounts& listener_counts = |
g_listener_counts.Get()[extension_id]; |
- std::string event_name(*v8::String::AsciiValue(args[0])); |
- bool is_manual = args[1]->BooleanValue(); |
if (--listener_counts[event_name] == 0) { |
content::RenderThread::Get()->Send( |
@@ -126,17 +154,164 @@ class ExtensionImpl : public ChromeV8Extension { |
// removed. If the context is the background page, and it removes the |
// last listener manually, then we assume that it is no longer interested |
// in being awakened for this event. |
- if (is_manual && self->IsLazyBackgroundPage(context->extension())) { |
+ if (is_manual && IsLazyBackgroundPage(context->extension())) { |
content::RenderThread::Get()->Send( |
new ExtensionHostMsg_RemoveLazyListener(extension_id, event_name)); |
} |
} |
+ return v8::Undefined(); |
+ } |
+ |
+ // MatcherID AttachFilteredEvent(string event_name, object filter) |
+ // event_name - Name of the event to attach. |
+ // filter - Which instances of the named event are we interested in. |
+ // returns the id assigned to the listener, which will be returned from calls |
+ // to MatchAgainstEventFilter where this listener matches. |
+ v8::Handle<v8::Value> AttachFilteredEvent(const v8::Arguments& args) { |
+ DCHECK_EQ(2, args.Length()); |
+ DCHECK(args[0]->IsString()); |
+ DCHECK(args[1]->IsObject()); |
+ |
+ const ChromeV8ContextSet& context_set = |
+ extension_dispatcher()->v8_context_set(); |
+ ChromeV8Context* context = context_set.GetCurrent(); |
+ DCHECK(context); |
+ if (!context) |
+ return v8::Integer::New(-1); |
+ |
+ std::string event_name = *v8::String::AsciiValue(args[0]); |
+ // This method throws an exception if it returns false. |
+ if (!extension_dispatcher()->CheckCurrentContextAccessToExtensionAPI( |
+ event_name)) |
+ return v8::Undefined(); |
+ |
+ std::string extension_id = context->GetExtensionID(); |
+ if (extension_id.empty()) |
+ return v8::Integer::New(-1); |
+ |
+ scoped_ptr<base::DictionaryValue> filter; |
+ scoped_ptr<content::V8ValueConverter> converter( |
+ content::V8ValueConverter::create()); |
+ |
+ base::DictionaryValue* filter_dict = NULL; |
+ base::Value* filter_value = converter->FromV8Value(args[1]->ToObject(), |
+ v8::Context::GetCurrent()); |
+ if (!filter_value->GetAsDictionary(&filter_dict)) { |
+ delete filter_value; |
battre
2012/06/20 09:50:46
I think filter_value is still lost if you don't ru
koz (OOO until 15th September)
2012/06/21 02:23:11
Is it? I thought that filter_value->GetAsDictionar
battre
2012/06/21 05:35:16
ah, you are right. I missed the "As" in GetAsDicti
|
+ return v8::Integer::New(-1); |
+ } |
+ |
+ filter.reset(filter_dict); |
+ int id = event_filter_->AddEventMatcher(event_name, ParseEventMatcher( |
+ filter.get())); |
+ |
+ // Only send IPCs the first time a filter gets added. |
+ if (AddFilter(event_name, extension_id, filter.get())) { |
+ bool lazy = IsLazyBackgroundPage(context->extension()); |
+ content::RenderThread::Get()->Send( |
+ new ExtensionHostMsg_AddFilteredListener(extension_id, event_name, |
+ *filter, lazy)); |
+ } |
+ |
+ return v8::Integer::New(id); |
+ } |
+ |
+ // Add a filter to |event_name| in |extension_id|, returning true if it |
+ // was the first filter for that event in that extension. |
+ bool AddFilter(const std::string& event_name, |
+ const std::string& extension_id, |
+ base::DictionaryValue* filter) { |
+ FilteredEventListenerCounts& counts = |
+ g_filtered_listener_counts.Get()[extension_id]; |
+ FilteredEventListenerCounts::iterator it = counts.find(event_name); |
+ if (it == counts.end()) |
+ counts[event_name].reset(new extensions::ValueCounter); |
+ |
+ int result = counts[event_name]->Add(*filter); |
+ return 1 == result; |
+ } |
+ |
+ // Remove a filter from |event_name| in |extension_id|, returning true if it |
+ // was the last filter for that event in that extension. |
+ bool RemoveFilter(const std::string& event_name, |
+ const std::string& extension_id, |
+ base::DictionaryValue* filter) { |
+ FilteredEventListenerCounts& counts = |
+ g_filtered_listener_counts.Get()[extension_id]; |
+ FilteredEventListenerCounts::iterator it = counts.find(event_name); |
+ if (it == counts.end()) |
+ return false; |
+ return 0 == it->second->Remove(*filter); |
+ } |
+ |
+ // void DetachFilteredEvent(int id, bool manual) |
+ // id - Id of the event to detach. |
+ // manual - false if this is part of the extension unload process where all |
+ // listeners are automatically detached. |
+ v8::Handle<v8::Value> DetachFilteredEvent(const v8::Arguments& args) { |
+ DCHECK_EQ(2, args.Length()); |
+ DCHECK(args[0]->IsInt32()); |
+ DCHECK(args[1]->IsBoolean()); |
+ bool is_manual = args[1]->BooleanValue(); |
+ const ChromeV8ContextSet& context_set = |
+ extension_dispatcher()->v8_context_set(); |
+ ChromeV8Context* context = context_set.GetCurrent(); |
+ if (!context) |
+ return v8::Undefined(); |
+ |
+ std::string extension_id = context->GetExtensionID(); |
+ if (extension_id.empty()) |
+ return v8::Undefined(); |
+ |
+ int matcher_id = args[0]->Int32Value(); |
+ extensions::EventMatcher* event_matcher = |
+ event_filter_->GetEventMatcher(matcher_id); |
+ |
+ const std::string& event_name = event_filter_->GetEventName(matcher_id); |
+ |
+ // Only send IPCs the last time a filter gets removed. |
+ if (RemoveFilter(event_name, extension_id, event_matcher->value())) { |
+ bool lazy = is_manual && IsLazyBackgroundPage(context->extension()); |
+ content::RenderThread::Get()->Send( |
+ new ExtensionHostMsg_RemoveFilteredListener(extension_id, event_name, |
+ *event_matcher->value(), |
+ lazy)); |
+ } |
+ |
+ event_filter_->RemoveEventMatcher(matcher_id); |
return v8::Undefined(); |
} |
- private: |
+ v8::Handle<v8::Value> MatchAgainstEventFilter(const v8::Arguments& args) { |
+ typedef std::set<extensions::EventFilter::MatcherID> MatcherIDs; |
+ |
+ std::string event_name = *v8::String::AsciiValue(args[0]->ToString()); |
+ extensions::EventFilteringInfo info = ParseFromObject(args[1]->ToObject()); |
+ MatcherIDs matched_event_filters = event_filter_->MatchEvent( |
+ event_name, info); |
+ v8::Handle<v8::Array> array(v8::Array::New(matched_event_filters.size())); |
+ int i = 0; |
+ for (MatcherIDs::iterator it = matched_event_filters.begin(); |
+ it != matched_event_filters.end(); ++it) { |
+ array->Set(v8::Integer::New(i++), v8::Integer::New(*it)); |
+ } |
+ return array; |
+ } |
+ |
+ extensions::EventFilteringInfo ParseFromObject( |
+ v8::Handle<v8::Object> object) { |
+ extensions::EventFilteringInfo info; |
+ v8::Handle<v8::String> url(v8::String::New("url")); |
+ if (object->Has(url)) { |
+ v8::Handle<v8::Value> url_value(object->Get(url)); |
+ info.SetURL(GURL(*v8::String::AsciiValue(url_value))); |
+ } |
+ return info; |
+ } |
+ private: |
+ extensions::EventFilter* event_filter_; |
bool IsLazyBackgroundPage(const Extension* extension) { |
content::RenderView* render_view = GetCurrentRenderView(); |
if (!render_view) |
@@ -146,10 +321,18 @@ class ExtensionImpl : public ChromeV8Extension { |
return (extension && extension->has_lazy_background_page() && |
helper->view_type() == chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); |
} |
+ |
+ scoped_ptr<extensions::EventMatcher> ParseEventMatcher( |
+ base::DictionaryValue* filter_dict) { |
+ return scoped_ptr<extensions::EventMatcher>(new extensions::EventMatcher( |
+ scoped_ptr<base::DictionaryValue>(filter_dict->DeepCopy()))); |
+ } |
}; |
} // namespace |
-ChromeV8Extension* EventBindings::Get(ExtensionDispatcher* dispatcher) { |
- return new ExtensionImpl(dispatcher); |
+// static |
+ChromeV8Extension* EventBindings::Get(ExtensionDispatcher* dispatcher, |
+ extensions::EventFilter* event_filter) { |
+ return new ExtensionImpl(dispatcher, event_filter); |
} |