Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(356)

Unified Diff: extensions/renderer/api_event_listeners.cc

Issue 2768093002: [Reland][Extensions Bindings] Add support for filtered events (Closed)
Patch Set: Fix Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « extensions/renderer/api_event_listeners.h ('k') | extensions/renderer/api_event_listeners_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: extensions/renderer/api_event_listeners.cc
diff --git a/extensions/renderer/api_event_listeners.cc b/extensions/renderer/api_event_listeners.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a59c9c6aa1fbcc2c05eeba41a319b4f9c201cde2
--- /dev/null
+++ b/extensions/renderer/api_event_listeners.cc
@@ -0,0 +1,254 @@
+// Copyright 2017 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/api_event_listeners.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "content/public/child/v8_value_converter.h"
+#include "extensions/common/event_filter.h"
+#include "extensions/common/event_filtering_info.h"
+#include "extensions/common/event_matcher.h"
+#include "gin/converter.h"
+
+namespace extensions {
+
+namespace {
+
+// TODO(devlin): The EventFilter supports adding EventMatchers associated with
+// an id. For now, we ignore it and add/return everything associated with this
+// constant. We should rethink that.
+const int kIgnoreRoutingId = 0;
+
+// Pseudo-validates the given |filter| and converts it into a
+// base::DictionaryValue. Returns true on success.
+// TODO(devlin): This "validation" is pretty terrible. It matches the JS
+// equivalent, but it's lousy and makes it easy for users to get it wrong.
+// We should generate an argument spec for it and match it exactly.
+bool ValidateFilter(v8::Local<v8::Context> context,
+ v8::Local<v8::Object> filter,
+ std::unique_ptr<base::DictionaryValue>* filter_dict,
+ std::string* error) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::HandleScope handle_scope(isolate);
+
+ if (filter.IsEmpty()) {
+ *filter_dict = base::MakeUnique<base::DictionaryValue>();
+ return true;
+ }
+
+ v8::Local<v8::Value> url_filter;
+ if (!filter->Get(context, gin::StringToSymbol(isolate, "url"))
+ .ToLocal(&url_filter)) {
+ return false;
+ }
+
+ if (!url_filter->IsUndefined() && !url_filter->IsArray()) {
+ *error = "filters.url should be an array.";
+ return false;
+ }
+
+ v8::Local<v8::Value> service_type;
+ if (!filter->Get(context, gin::StringToSymbol(isolate, "serviceType"))
+ .ToLocal(&service_type)) {
+ return false;
+ }
+
+ if (!service_type->IsUndefined() && !service_type->IsString()) {
+ *error = "filters.serviceType should be a string.";
+ return false;
+ }
+
+ std::unique_ptr<content::V8ValueConverter> converter(
+ content::V8ValueConverter::create());
+ std::unique_ptr<base::Value> value = converter->FromV8Value(filter, context);
+ if (!value || !value->is_dict()) {
+ *error = "could not convert filter.";
+ return false;
+ }
+
+ *filter_dict = base::DictionaryValue::From(std::move(value));
+ return true;
+}
+
+} // namespace
+
+UnfilteredEventListeners::UnfilteredEventListeners(
+ const ListenersUpdated& listeners_updated)
+ : listeners_updated_(listeners_updated) {}
+UnfilteredEventListeners::~UnfilteredEventListeners() = default;
+
+bool UnfilteredEventListeners::AddListener(v8::Local<v8::Function> listener,
+ v8::Local<v8::Object> filter,
+ v8::Local<v8::Context> context,
+ std::string* error) {
+ // |filter| should be checked before getting here.
+ DCHECK(filter.IsEmpty())
+ << "Filtered events should use FilteredEventListeners";
+
+ if (HasListener(listener))
+ return false;
+
+ listeners_.push_back(
+ v8::Global<v8::Function>(context->GetIsolate(), listener));
+ if (listeners_.size() == 1) {
+ listeners_updated_.Run(binding::EventListenersChanged::HAS_LISTENERS,
+ nullptr, context);
+ }
+
+ return true;
+}
+
+void UnfilteredEventListeners::RemoveListener(v8::Local<v8::Function> listener,
+ v8::Local<v8::Context> context) {
+ auto iter = std::find(listeners_.begin(), listeners_.end(), listener);
+ if (iter == listeners_.end())
+ return;
+
+ listeners_.erase(iter);
+ if (listeners_.empty()) {
+ listeners_updated_.Run(binding::EventListenersChanged::NO_LISTENERS,
+ nullptr, context);
+ }
+}
+
+bool UnfilteredEventListeners::HasListener(v8::Local<v8::Function> listener) {
+ return std::find(listeners_.begin(), listeners_.end(), listener) !=
+ listeners_.end();
+}
+
+size_t UnfilteredEventListeners::GetNumListeners() {
+ return listeners_.size();
+}
+
+std::vector<v8::Local<v8::Function>> UnfilteredEventListeners::GetListeners(
+ const EventFilteringInfo* filter,
+ v8::Local<v8::Context> context) {
+ std::vector<v8::Local<v8::Function>> listeners;
+ listeners.reserve(listeners_.size());
+ for (const auto& listener : listeners_)
+ listeners.push_back(listener.Get(context->GetIsolate()));
+ return listeners;
+}
+
+void UnfilteredEventListeners::Invalidate(v8::Local<v8::Context> context) {
+ if (!listeners_.empty()) {
+ listeners_.clear();
+ listeners_updated_.Run(binding::EventListenersChanged::NO_LISTENERS,
+ nullptr, context);
+ }
+}
+
+struct FilteredEventListeners::ListenerData {
+ bool operator==(v8::Local<v8::Function> other_function) const {
+ // Note that we only consider the listener function here, and not the
+ // filter. This implies that it's invalid to try and add the same
+ // function for multiple filters.
+ // TODO(devlin): It's always been this way, but should it be?
+ return function == other_function;
+ }
+
+ v8::Global<v8::Function> function;
+ int filter_id;
+};
+
+FilteredEventListeners::FilteredEventListeners(
+ const ListenersUpdated& listeners_updated,
+ const std::string& event_name,
+ EventFilter* event_filter)
+ : listeners_updated_(listeners_updated),
+ event_name_(event_name),
+ event_filter_(event_filter) {}
+FilteredEventListeners::~FilteredEventListeners() = default;
+
+bool FilteredEventListeners::AddListener(v8::Local<v8::Function> listener,
+ v8::Local<v8::Object> filter,
+ v8::Local<v8::Context> context,
+ std::string* error) {
+ if (HasListener(listener))
+ return false;
+
+ std::unique_ptr<base::DictionaryValue> filter_dict;
+ if (!ValidateFilter(context, filter, &filter_dict, error))
+ return false;
+
+ int filter_id = event_filter_->AddEventMatcher(
+ event_name_,
+ base::MakeUnique<EventMatcher>(std::move(filter_dict), kIgnoreRoutingId));
+
+ if (filter_id == -1) {
+ *error = "Could not add listener";
+ return false;
+ }
+
+ const EventMatcher* matcher = event_filter_->GetEventMatcher(filter_id);
+ DCHECK(matcher);
+ listeners_.push_back(
+ {v8::Global<v8::Function>(context->GetIsolate(), listener), filter_id});
+ if (value_counter_.Add(*matcher->value())) {
+ listeners_updated_.Run(binding::EventListenersChanged::HAS_LISTENERS,
+ matcher->value(), context);
+ }
+
+ return true;
+}
+
+void FilteredEventListeners::RemoveListener(v8::Local<v8::Function> listener,
+ v8::Local<v8::Context> context) {
+ auto iter = std::find(listeners_.begin(), listeners_.end(), listener);
+ if (iter == listeners_.end())
+ return;
+
+ ListenerData data = std::move(*iter);
+ listeners_.erase(iter);
+
+ InvalidateListener(data, context);
+}
+
+bool FilteredEventListeners::HasListener(v8::Local<v8::Function> listener) {
+ return std::find(listeners_.begin(), listeners_.end(), listener) !=
+ listeners_.end();
+}
+
+size_t FilteredEventListeners::GetNumListeners() {
+ return listeners_.size();
+}
+
+std::vector<v8::Local<v8::Function>> FilteredEventListeners::GetListeners(
+ const EventFilteringInfo* filter,
+ v8::Local<v8::Context> context) {
+ std::set<int> ids = event_filter_->MatchEvent(
+ event_name_, filter ? *filter : EventFilteringInfo(), kIgnoreRoutingId);
+
+ std::vector<v8::Local<v8::Function>> listeners;
+ listeners.reserve(ids.size());
+ for (const auto& listener : listeners_) {
+ if (ids.count(listener.filter_id))
+ listeners.push_back(listener.function.Get(context->GetIsolate()));
+ }
+ return listeners;
+}
+
+void FilteredEventListeners::Invalidate(v8::Local<v8::Context> context) {
+ for (const auto& listener : listeners_)
+ InvalidateListener(listener, context);
+ listeners_.clear();
+}
+
+void FilteredEventListeners::InvalidateListener(
+ const ListenerData& listener,
+ v8::Local<v8::Context> context) {
+ EventMatcher* matcher = event_filter_->GetEventMatcher(listener.filter_id);
+ DCHECK(matcher);
+ if (value_counter_.Remove(*matcher->value())) {
+ listeners_updated_.Run(binding::EventListenersChanged::NO_LISTENERS,
+ matcher->value(), context);
+ }
+
+ event_filter_->RemoveEventMatcher(listener.filter_id);
+}
+
+} // namespace extensions
« no previous file with comments | « extensions/renderer/api_event_listeners.h ('k') | extensions/renderer/api_event_listeners_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698