Chromium Code Reviews| 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..e961e14609f0b6b1563cd4857d99746ce0b8f3d9 100644 |
| --- a/chrome/renderer/extensions/event_bindings.cc |
| +++ b/chrome/renderer/extensions/event_bindings.cc |
| @@ -6,11 +6,17 @@ |
| #include <vector> |
| +#include "base/callback.h" |
| #include "base/basictypes.h" |
| +#include "base/memory/scoped_ptr.h" |
| #include "base/lazy_instance.h" |
| +#include "base/memory/scoped_ptr.h" |
| #include "base/message_loop.h" |
| +#include "chrome/common/extensions/matcher/url_matcher.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_set.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/common/view_type.h" |
| #include "chrome/renderer/extensions/chrome_v8_context.h" |
| @@ -21,6 +27,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 +55,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<ValueSet> > |
| + 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); |
| + explicit ExtensionImpl(ExtensionDispatcher* dispatcher, |
|
Matt Perry
2012/06/13 01:24:27
drop explicit
koz (OOO until 15th September)
2012/06/14 02:15:55
Done.
|
| + 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 +120,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 +146,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 +156,146 @@ 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(); |
| + } |
| + |
| + // void 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. |
| + 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(); |
| + if (!context) |
| + return v8::Integer::New(-1); |
| + |
| + std::string event_name = *v8::String::AsciiValue(args[0]); |
| + std::string extension_id = context->GetExtensionID(); |
| + if (extension_id.empty()) |
| + return v8::Undefined(); |
| + |
| + scoped_ptr<base::DictionaryValue> filter; |
| + { |
| + scoped_ptr<content::V8ValueConverter> converter( |
| + content::V8ValueConverter::create()); |
| + |
| + base::DictionaryValue* filter_dict; |
| + base::Value* filter_value = |
| + converter->FromV8Value(args[1]->ToObject(), |
| + v8::Context::GetCurrent()); |
| + CHECK(filter_value->GetAsDictionary(&filter_dict)); |
| + 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); |
| + } |
| + |
| + bool AddFilter(const std::string& event_name, |
| + const std::string& extension_id, |
| + base::DictionaryValue* filter) { |
| + FilteredEventListenerCounts::iterator it = |
| + g_filtered_listener_counts.Get()[extension_id].find(event_name); |
| + if (it == g_filtered_listener_counts.Get()[extension_id].end()) |
| + g_filtered_listener_counts.Get()[extension_id][event_name].reset( |
| + new ValueSet); |
| + |
| + int result = g_filtered_listener_counts.Get()[extension_id][event_name]-> |
| + Add(filter); |
| + return 1 == result; |
| + } |
| + |
| + bool RemoveFilter(const std::string& event_name, |
| + const std::string& extension_id, |
| + base::DictionaryValue* filter) { |
| + int result = g_filtered_listener_counts.Get()[extension_id][event_name]-> |
| + Remove(filter); |
| + return 0 == result; |
| + } |
| + |
| + // 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::Integer::New(-1); |
| + |
| + 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) { |
| + v8::Handle<v8::Value> url_value(object->Get(v8::String::New("url"))); |
| + extensions::EventFilteringInfo info; |
| + 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 +305,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); |
| } |