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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "extensions/renderer/api_event_listeners.h"
6
7 #include <algorithm>
8 #include <memory>
9
10 #include "base/memory/ptr_util.h"
11 #include "content/public/child/v8_value_converter.h"
12 #include "extensions/common/event_filter.h"
13 #include "extensions/common/event_filtering_info.h"
14 #include "extensions/common/event_matcher.h"
15 #include "gin/converter.h"
16
17 namespace extensions {
18
19 namespace {
20
21 // TODO(devlin): The EventFilter supports adding EventMatchers associated with
22 // an id. For now, we ignore it and add/return everything associated with this
23 // constant. We should rethink that.
24 const int kIgnoreRoutingId = 0;
25
26 // Pseudo-validates the given |filter| and converts it into a
27 // base::DictionaryValue. Returns true on success.
28 // TODO(devlin): This "validation" is pretty terrible. It matches the JS
29 // equivalent, but it's lousy and makes it easy for users to get it wrong.
30 // We should generate an argument spec for it and match it exactly.
31 bool ValidateFilter(v8::Local<v8::Context> context,
32 v8::Local<v8::Object> filter,
33 std::unique_ptr<base::DictionaryValue>* filter_dict,
34 std::string* error) {
35 v8::Isolate* isolate = context->GetIsolate();
36 v8::HandleScope handle_scope(isolate);
37
38 if (filter.IsEmpty()) {
39 *filter_dict = base::MakeUnique<base::DictionaryValue>();
40 return true;
41 }
42
43 v8::Local<v8::Value> url_filter;
44 if (!filter->Get(context, gin::StringToSymbol(isolate, "url"))
45 .ToLocal(&url_filter)) {
46 return false;
47 }
48
49 if (!url_filter->IsUndefined() && !url_filter->IsArray()) {
50 *error = "filters.url should be an array.";
51 return false;
52 }
53
54 v8::Local<v8::Value> service_type;
55 if (!filter->Get(context, gin::StringToSymbol(isolate, "serviceType"))
56 .ToLocal(&service_type)) {
57 return false;
58 }
59
60 if (!service_type->IsUndefined() && !service_type->IsString()) {
61 *error = "filters.serviceType should be a string.";
62 return false;
63 }
64
65 std::unique_ptr<content::V8ValueConverter> converter(
66 content::V8ValueConverter::create());
67 std::unique_ptr<base::Value> value = converter->FromV8Value(filter, context);
68 if (!value || !value->is_dict()) {
69 *error = "could not convert filter.";
70 return false;
71 }
72
73 *filter_dict = base::DictionaryValue::From(std::move(value));
74 return true;
75 }
76
77 } // namespace
78
79 UnfilteredEventListeners::UnfilteredEventListeners(
80 const ListenersUpdated& listeners_updated)
81 : listeners_updated_(listeners_updated) {}
82 UnfilteredEventListeners::~UnfilteredEventListeners() = default;
83
84 bool UnfilteredEventListeners::AddListener(v8::Local<v8::Function> listener,
85 v8::Local<v8::Object> filter,
86 v8::Local<v8::Context> context,
87 std::string* error) {
88 // |filter| should be checked before getting here.
89 DCHECK(filter.IsEmpty())
90 << "Filtered events should use FilteredEventListeners";
91
92 if (HasListener(listener))
93 return false;
94
95 listeners_.push_back(
96 v8::Global<v8::Function>(context->GetIsolate(), listener));
97 if (listeners_.size() == 1) {
98 listeners_updated_.Run(binding::EventListenersChanged::HAS_LISTENERS,
99 nullptr, context);
100 }
101
102 return true;
103 }
104
105 void UnfilteredEventListeners::RemoveListener(v8::Local<v8::Function> listener,
106 v8::Local<v8::Context> context) {
107 auto iter = std::find(listeners_.begin(), listeners_.end(), listener);
108 if (iter == listeners_.end())
109 return;
110
111 listeners_.erase(iter);
112 if (listeners_.empty()) {
113 listeners_updated_.Run(binding::EventListenersChanged::NO_LISTENERS,
114 nullptr, context);
115 }
116 }
117
118 bool UnfilteredEventListeners::HasListener(v8::Local<v8::Function> listener) {
119 return std::find(listeners_.begin(), listeners_.end(), listener) !=
120 listeners_.end();
121 }
122
123 size_t UnfilteredEventListeners::GetNumListeners() {
124 return listeners_.size();
125 }
126
127 std::vector<v8::Local<v8::Function>> UnfilteredEventListeners::GetListeners(
128 const EventFilteringInfo* filter,
129 v8::Local<v8::Context> context) {
130 std::vector<v8::Local<v8::Function>> listeners;
131 listeners.reserve(listeners_.size());
132 for (const auto& listener : listeners_)
133 listeners.push_back(listener.Get(context->GetIsolate()));
134 return listeners;
135 }
136
137 void UnfilteredEventListeners::Invalidate(v8::Local<v8::Context> context) {
138 if (!listeners_.empty()) {
139 listeners_.clear();
140 listeners_updated_.Run(binding::EventListenersChanged::NO_LISTENERS,
141 nullptr, context);
142 }
143 }
144
145 struct FilteredEventListeners::ListenerData {
146 bool operator==(v8::Local<v8::Function> other_function) const {
147 // Note that we only consider the listener function here, and not the
148 // filter. This implies that it's invalid to try and add the same
149 // function for multiple filters.
150 // TODO(devlin): It's always been this way, but should it be?
151 return function == other_function;
152 }
153
154 v8::Global<v8::Function> function;
155 int filter_id;
156 };
157
158 FilteredEventListeners::FilteredEventListeners(
159 const ListenersUpdated& listeners_updated,
160 const std::string& event_name,
161 EventFilter* event_filter)
162 : listeners_updated_(listeners_updated),
163 event_name_(event_name),
164 event_filter_(event_filter) {}
165 FilteredEventListeners::~FilteredEventListeners() = default;
166
167 bool FilteredEventListeners::AddListener(v8::Local<v8::Function> listener,
168 v8::Local<v8::Object> filter,
169 v8::Local<v8::Context> context,
170 std::string* error) {
171 if (HasListener(listener))
172 return false;
173
174 std::unique_ptr<base::DictionaryValue> filter_dict;
175 if (!ValidateFilter(context, filter, &filter_dict, error))
176 return false;
177
178 int filter_id = event_filter_->AddEventMatcher(
179 event_name_,
180 base::MakeUnique<EventMatcher>(std::move(filter_dict), kIgnoreRoutingId));
181
182 if (filter_id == -1) {
183 *error = "Could not add listener";
184 return false;
185 }
186
187 const EventMatcher* matcher = event_filter_->GetEventMatcher(filter_id);
188 DCHECK(matcher);
189 listeners_.push_back(
190 {v8::Global<v8::Function>(context->GetIsolate(), listener), filter_id});
191 if (value_counter_.Add(*matcher->value())) {
192 listeners_updated_.Run(binding::EventListenersChanged::HAS_LISTENERS,
193 matcher->value(), context);
194 }
195
196 return true;
197 }
198
199 void FilteredEventListeners::RemoveListener(v8::Local<v8::Function> listener,
200 v8::Local<v8::Context> context) {
201 auto iter = std::find(listeners_.begin(), listeners_.end(), listener);
202 if (iter == listeners_.end())
203 return;
204
205 ListenerData data = std::move(*iter);
206 listeners_.erase(iter);
207
208 InvalidateListener(data, context);
209 }
210
211 bool FilteredEventListeners::HasListener(v8::Local<v8::Function> listener) {
212 return std::find(listeners_.begin(), listeners_.end(), listener) !=
213 listeners_.end();
214 }
215
216 size_t FilteredEventListeners::GetNumListeners() {
217 return listeners_.size();
218 }
219
220 std::vector<v8::Local<v8::Function>> FilteredEventListeners::GetListeners(
221 const EventFilteringInfo* filter,
222 v8::Local<v8::Context> context) {
223 std::set<int> ids = event_filter_->MatchEvent(
224 event_name_, filter ? *filter : EventFilteringInfo(), kIgnoreRoutingId);
225
226 std::vector<v8::Local<v8::Function>> listeners;
227 listeners.reserve(ids.size());
228 for (const auto& listener : listeners_) {
229 if (ids.count(listener.filter_id))
230 listeners.push_back(listener.function.Get(context->GetIsolate()));
231 }
232 return listeners;
233 }
234
235 void FilteredEventListeners::Invalidate(v8::Local<v8::Context> context) {
236 for (const auto& listener : listeners_)
237 InvalidateListener(listener, context);
238 listeners_.clear();
239 }
240
241 void FilteredEventListeners::InvalidateListener(
242 const ListenerData& listener,
243 v8::Local<v8::Context> context) {
244 EventMatcher* matcher = event_filter_->GetEventMatcher(listener.filter_id);
245 DCHECK(matcher);
246 if (value_counter_.Remove(*matcher->value())) {
247 listeners_updated_.Run(binding::EventListenersChanged::NO_LISTENERS,
248 matcher->value(), context);
249 }
250
251 event_filter_->RemoveEventMatcher(listener.filter_id);
252 }
253
254 } // namespace extensions
OLDNEW
« 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