summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorrockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-15 18:20:26 +0000
committerrockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-15 18:20:26 +0000
commitc6c301add590f3dc46fbc6c42b278d3b87f13df1 (patch)
treec6c8bbac01e61c3a61213bd5a686c37add21b3f3 /extensions
parentf9966b0180a068b508030f10c4e353fbeb02165b (diff)
downloadchromium_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.cc56
-rw-r--r--extensions/common/value_counter.h71
-rw-r--r--extensions/extensions.gyp4
-rw-r--r--extensions/renderer/event_bindings.cc324
-rw-r--r--extensions/renderer/event_bindings.h24
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_