diff options
author | rockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-15 18:20:26 +0000 |
---|---|---|
committer | rockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-15 18:20:26 +0000 |
commit | c6c301add590f3dc46fbc6c42b278d3b87f13df1 (patch) | |
tree | c6c8bbac01e61c3a61213bd5a686c37add21b3f3 /extensions | |
parent | f9966b0180a068b508030f10c4e353fbeb02165b (diff) | |
download | chromium_src-c6c301add590f3dc46fbc6c42b278d3b87f13df1.zip chromium_src-c6c301add590f3dc46fbc6c42b278d3b87f13df1.tar.gz chromium_src-c6c301add590f3dc46fbc6c42b278d3b87f13df1.tar.bz2 |
Move EventBindings to //extensions
This moves the EventBindings class to //extensions
along with one of its dependencies (ValueCounter).
EventBindings::Create now returns an ObjectBackedNativeHandler
rather than a ChromeV8Extension.
BUG=359836
TBR=phajdan.jr@chromium.org for chrome/test
Review URL: https://codereview.chromium.org/237723004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@263928 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions')
-rw-r--r-- | extensions/common/value_counter.cc | 56 | ||||
-rw-r--r-- | extensions/common/value_counter.h | 71 | ||||
-rw-r--r-- | extensions/extensions.gyp | 4 | ||||
-rw-r--r-- | extensions/renderer/event_bindings.cc | 324 | ||||
-rw-r--r-- | extensions/renderer/event_bindings.h | 24 |
5 files changed, 479 insertions, 0 deletions
diff --git a/extensions/common/value_counter.cc b/extensions/common/value_counter.cc new file mode 100644 index 0000000..4138f25 --- /dev/null +++ b/extensions/common/value_counter.cc @@ -0,0 +1,56 @@ +// Copyright 2014 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 "extensions/common/value_counter.h" + +#include <algorithm> + +#include "base/values.h" + +namespace extensions { + +ValueCounter::ValueCounter() {} + +ValueCounter::~ValueCounter() {} + +ValueCounter::Entry::Entry(const base::Value& value) + : value_(value.DeepCopy()), count_(1) {} + +ValueCounter::Entry::~Entry() {} + +int ValueCounter::Entry::Increment() { return ++count_; } + +int ValueCounter::Entry::Decrement() { return --count_; } + +int ValueCounter::Add(const base::Value& value) { return AddImpl(value, true); } + +int ValueCounter::Remove(const base::Value& value) { + for (EntryList::iterator it = entries_.begin(); it != entries_.end(); it++) { + (*it)->value()->GetType(); + if ((*it)->value()->Equals(&value)) { + int remaining = (*it)->Decrement(); + if (remaining == 0) { + std::swap(*it, entries_.back()); + entries_.pop_back(); + } + return remaining; + } + } + return 0; +} + +int ValueCounter::AddIfMissing(const base::Value& value) { + return AddImpl(value, false); +} + +int ValueCounter::AddImpl(const base::Value& value, bool increment) { + for (EntryList::iterator it = entries_.begin(); it != entries_.end(); it++) { + if ((*it)->value()->Equals(&value)) + return increment ? (*it)->Increment() : (*it)->count(); + } + entries_.push_back(linked_ptr<Entry>(new Entry(value))); + return 1; +} + +} // namespace extensions diff --git a/extensions/common/value_counter.h b/extensions/common/value_counter.h new file mode 100644 index 0000000..948cd30 --- /dev/null +++ b/extensions/common/value_counter.h @@ -0,0 +1,71 @@ +// Copyright 2014 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 EXTENSIONS_COMMON_VALUE_COUNTER_H_ +#define EXTENSIONS_COMMON_VALUE_COUNTER_H_ + +#include <vector> + +#include "base/memory/linked_ptr.h" + +namespace base { +class Value; +} + +namespace extensions { + +// Keeps a running count of Values, like map<Value, int>. Adding / removing +// values increments / decrements the count associated with a given Value. +// +// Add() and Remove() are linear in the number of Values in the ValueCounter, +// because there is no operator<() defined on Value, so we must iterate to find +// whether a Value is equal to an existing one. +class ValueCounter { + public: + ValueCounter(); + ~ValueCounter(); + + // Adds |value| to the set and returns how many equal values are in the set + // after. Does not take ownership of |value|. In the case where a Value equal + // to |value| doesn't already exist in this map, this function makes a + // DeepCopy() of |value|. + int Add(const base::Value& value); + + // Removes |value| from the set and returns how many equal values are in + // the set after. + int Remove(const base::Value& value); + + // Same as Add() but only performs the add if the value isn't present. + int AddIfMissing(const base::Value& value); + + private: + class Entry { + public: + explicit Entry(const base::Value& value); + ~Entry(); + + int Increment(); + int Decrement(); + + const base::Value* value() const { return value_.get(); } + int count() const { return count_; } + + private: + linked_ptr<base::Value> value_; + int count_; + + DISALLOW_COPY_AND_ASSIGN(Entry); + }; + typedef std::vector<linked_ptr<Entry> > EntryList; + + int AddImpl(const base::Value& value, bool increment); + + EntryList entries_; + + DISALLOW_COPY_AND_ASSIGN(ValueCounter); +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_VALUE_COUNTER_H_ diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index cd8a01a..e16b516 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -194,6 +194,8 @@ 'common/url_pattern_set.h', 'common/user_script.cc', 'common/user_script.h', + 'common/value_counter.cc', + 'common/value_counter.h', 'common/view_type.cc', 'common/view_type.h', ], @@ -415,6 +417,8 @@ 'sources': [ 'renderer/console.cc', 'renderer/console.h', + 'renderer/event_bindings.cc', + 'renderer/event_bindings.h', 'renderer/module_system.cc', 'renderer/module_system.h', 'renderer/native_handler.cc', diff --git a/extensions/renderer/event_bindings.cc b/extensions/renderer/event_bindings.cc new file mode 100644 index 0000000..0e675ee --- /dev/null +++ b/extensions/renderer/event_bindings.cc @@ -0,0 +1,324 @@ +// Copyright 2014 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 "extensions/renderer/event_bindings.h" + +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/renderer/extensions/dispatcher.h" +#include "chrome/renderer/extensions/extension_helper.h" +#include "content/public/renderer/render_thread.h" +#include "content/public/renderer/render_view.h" +#include "content/public/renderer/v8_value_converter.h" +#include "extensions/common/event_filter.h" +#include "extensions/common/extension.h" +#include "extensions/common/extension_messages.h" +#include "extensions/common/manifest_handlers/background_info.h" +#include "extensions/common/value_counter.h" +#include "extensions/renderer/object_backed_native_handler.h" +#include "url/gurl.h" +#include "v8/include/v8.h" + +namespace extensions { + +namespace { + +// A map of event names to the number of contexts listening to that event. +// We notify the browser about event listeners when we transition between 0 +// and 1. +typedef std::map<std::string, int> EventListenerCounts; + +// A map of extension IDs to listener counts for that extension. +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<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; + +base::LazyInstance<EventFilter> g_event_filter = LAZY_INSTANCE_INITIALIZER; + +// TODO(koz): Merge this into EventBindings. +class ExtensionImpl : public ObjectBackedNativeHandler { + public: + explicit ExtensionImpl(Dispatcher* dispatcher, ScriptContext* context) + : ObjectBackedNativeHandler(context), dispatcher_(dispatcher) { + 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))); + } + + virtual ~ExtensionImpl() {} + + // Attach an event name to an object. + void AttachEvent(const v8::FunctionCallbackInfo<v8::Value>& args) { + CHECK_EQ(1, args.Length()); + CHECK(args[0]->IsString()); + + std::string event_name = *v8::String::Utf8Value(args[0]->ToString()); + + if (!dispatcher_->CheckContextAccessToExtensionAPI(event_name, context())) + return; + + std::string extension_id = context()->GetExtensionID(); + EventListenerCounts& listener_counts = + g_listener_counts.Get()[extension_id]; + if (++listener_counts[event_name] == 1) { + content::RenderThread::Get()->Send( + new ExtensionHostMsg_AddListener(extension_id, event_name)); + } + + // 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 (IsLazyBackgroundContext(context())) { + content::RenderThread::Get()->Send( + new ExtensionHostMsg_AddLazyListener(extension_id, event_name)); + } + } + + void DetachEvent(const v8::FunctionCallbackInfo<v8::Value>& args) { + CHECK_EQ(2, args.Length()); + CHECK(args[0]->IsString()); + CHECK(args[1]->IsBoolean()); + + std::string event_name = *v8::String::Utf8Value(args[0]); + bool is_manual = args[1]->BooleanValue(); + + std::string extension_id = context()->GetExtensionID(); + EventListenerCounts& listener_counts = + g_listener_counts.Get()[extension_id]; + + if (--listener_counts[event_name] == 0) { + content::RenderThread::Get()->Send( + new ExtensionHostMsg_RemoveListener(extension_id, event_name)); + } + + // DetachEvent is called when the last listener for the context is + // 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 && IsLazyBackgroundContext(context())) { + content::RenderThread::Get()->Send( + new ExtensionHostMsg_RemoveLazyListener(extension_id, event_name)); + } + } + + // 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. + void AttachFilteredEvent(const v8::FunctionCallbackInfo<v8::Value>& args) { + CHECK_EQ(2, args.Length()); + CHECK(args[0]->IsString()); + CHECK(args[1]->IsObject()); + + std::string event_name = *v8::String::Utf8Value(args[0]); + + // This method throws an exception if it returns false. + if (!dispatcher_->CheckContextAccessToExtensionAPI(event_name, context())) + return; + + std::string extension_id = context()->GetExtensionID(); + if (extension_id.empty()) { + args.GetReturnValue().Set(static_cast<int32_t>(-1)); + return; + } + + 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(), context()->v8_context()); + if (!filter_value) { + args.GetReturnValue().Set(static_cast<int32_t>(-1)); + return; + } + if (!filter_value->GetAsDictionary(&filter_dict)) { + delete filter_value; + args.GetReturnValue().Set(static_cast<int32_t>(-1)); + return; + } + + filter.reset(filter_dict); + EventFilter& event_filter = g_event_filter.Get(); + 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 = IsLazyBackgroundContext(context()); + content::RenderThread::Get()->Send( + new ExtensionHostMsg_AddFilteredListener( + extension_id, event_name, *filter, lazy)); + } + + args.GetReturnValue().Set(static_cast<int32_t>(id)); + } + + // Add a filter to |event_name| in |extension_id|, returning true if it + // was the first filter for that event in that extension. + static 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 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. + static 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. + void DetachFilteredEvent(const v8::FunctionCallbackInfo<v8::Value>& args) { + CHECK_EQ(2, args.Length()); + CHECK(args[0]->IsInt32()); + CHECK(args[1]->IsBoolean()); + bool is_manual = args[1]->BooleanValue(); + + std::string extension_id = context()->GetExtensionID(); + if (extension_id.empty()) + return; + + int matcher_id = args[0]->Int32Value(); + EventFilter& event_filter = g_event_filter.Get(); + 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 && IsLazyBackgroundContext(context()); + content::RenderThread::Get()->Send( + new ExtensionHostMsg_RemoveFilteredListener( + extension_id, event_name, *event_matcher->value(), lazy)); + } + + event_filter.RemoveEventMatcher(matcher_id); + } + + void MatchAgainstEventFilter( + const v8::FunctionCallbackInfo<v8::Value>& args) { + v8::Isolate* isolate = args.GetIsolate(); + typedef std::set<EventFilter::MatcherID> MatcherIDs; + EventFilter& event_filter = g_event_filter.Get(); + std::string event_name = *v8::String::Utf8Value(args[0]->ToString()); + EventFilteringInfo info = ParseFromObject(args[1]->ToObject(), isolate); + // Only match events routed to this context's RenderView or ones that don't + // have a routingId in their filter. + MatcherIDs matched_event_filters = event_filter.MatchEvent( + event_name, info, context()->GetRenderView()->GetRoutingID()); + v8::Handle<v8::Array> array( + v8::Array::New(isolate, 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(isolate, i++), + v8::Integer::New(isolate, *it)); + } + args.GetReturnValue().Set(array); + } + + static EventFilteringInfo ParseFromObject(v8::Handle<v8::Object> object, + v8::Isolate* isolate) { + EventFilteringInfo info; + v8::Handle<v8::String> url(v8::String::NewFromUtf8(isolate, "url")); + if (object->Has(url)) { + v8::Handle<v8::Value> url_value(object->Get(url)); + info.SetURL(GURL(*v8::String::Utf8Value(url_value))); + } + v8::Handle<v8::String> instance_id( + v8::String::NewFromUtf8(isolate, "instanceId")); + if (object->Has(instance_id)) { + v8::Handle<v8::Value> instance_id_value(object->Get(instance_id)); + info.SetInstanceID(instance_id_value->IntegerValue()); + } + v8::Handle<v8::String> service_type( + v8::String::NewFromUtf8(isolate, "serviceType")); + if (object->Has(service_type)) { + v8::Handle<v8::Value> service_type_value(object->Get(service_type)); + info.SetServiceType(*v8::String::Utf8Value(service_type_value)); + } + return info; + } + + private: + static bool IsLazyBackgroundContext(ScriptContext* context) { + content::RenderView* render_view = context->GetRenderView(); + if (!render_view) + return false; + ExtensionHelper* helper = ExtensionHelper::Get(render_view); + const Extension* extension = context->extension(); + return (extension && BackgroundInfo::HasLazyBackgroundPage(extension) && + helper->view_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); + } + + scoped_ptr<EventMatcher> ParseEventMatcher( + base::DictionaryValue* filter_dict) { + return scoped_ptr<EventMatcher>(new EventMatcher( + scoped_ptr<base::DictionaryValue>(filter_dict->DeepCopy()), + context()->GetRenderView()->GetRoutingID())); + } + + // Not owned. + Dispatcher* dispatcher_; +}; + +} // namespace + +// static +ObjectBackedNativeHandler* EventBindings::Create(Dispatcher* dispatcher, + ScriptContext* context) { + return new ExtensionImpl(dispatcher, context); +} + +} // namespace extensions diff --git a/extensions/renderer/event_bindings.h b/extensions/renderer/event_bindings.h new file mode 100644 index 0000000..370724d --- /dev/null +++ b/extensions/renderer/event_bindings.h @@ -0,0 +1,24 @@ +// Copyright 2014 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 EXTENSIONS_RENDERER_EVENT_BINDINGS_H_ +#define EXTENSIONS_RENDERER_EVENT_BINDINGS_H_ + +#include "v8/include/v8.h" + +namespace extensions { +class Dispatcher; +class ObjectBackedNativeHandler; +class ScriptContext; + +// This class deals with the javascript bindings related to Event objects. +class EventBindings { + public: + static ObjectBackedNativeHandler* Create(Dispatcher* dispatcher, + ScriptContext* context); +}; + +} // namespace extensions + +#endif // EXTENSIONS_RENDERER_EVENT_BINDINGS_H_ |