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

Side by Side Diff: extensions/renderer/api_event_handler.cc

Issue 2947463002: [Extensions Bindings] Add a bindings/ subdirectory under renderer (Closed)
Patch Set: . Created 3 years, 6 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 2016 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_handler.h"
6
7 #include <algorithm>
8 #include <map>
9 #include <memory>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/logging.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/supports_user_data.h"
16 #include "base/values.h"
17 #include "content/public/child/v8_value_converter.h"
18 #include "extensions/renderer/api_event_listeners.h"
19 #include "extensions/renderer/event_emitter.h"
20 #include "gin/handle.h"
21 #include "gin/per_context_data.h"
22
23 namespace extensions {
24
25 namespace {
26
27 void DoNothingOnListenersChanged(binding::EventListenersChanged change,
28 const base::DictionaryValue* filter,
29 bool was_manual,
30 v8::Local<v8::Context> context) {}
31
32 const char kExtensionAPIEventPerContextKey[] = "extension_api_events";
33
34 struct APIEventPerContextData : public base::SupportsUserData::Data {
35 APIEventPerContextData(v8::Isolate* isolate) : isolate(isolate) {}
36 ~APIEventPerContextData() override {
37 DCHECK(emitters.empty())
38 << "|emitters| should have been cleared by InvalidateContext()";
39 DCHECK(massagers.empty())
40 << "|massagers| should have been cleared by InvalidateContext()";
41 DCHECK(anonymous_emitters.empty())
42 << "|anonymous_emitters| should have been cleared by "
43 << "InvalidateContext()";
44 }
45
46 // The associated v8::Isolate. Since this object is cleaned up at context
47 // destruction, this should always be valid.
48 v8::Isolate* isolate;
49
50 // A map from event name -> event emitter.
51 std::map<std::string, v8::Global<v8::Object>> emitters;
52
53 // A map from event name -> argument massager.
54 std::map<std::string, v8::Global<v8::Function>> massagers;
55
56 // The collection of anonymous events.
57 std::vector<v8::Global<v8::Object>> anonymous_emitters;
58 };
59
60 APIEventPerContextData* GetContextData(v8::Local<v8::Context> context,
61 bool should_create) {
62 gin::PerContextData* per_context_data = gin::PerContextData::From(context);
63 if (!per_context_data)
64 return nullptr;
65 auto* data = static_cast<APIEventPerContextData*>(
66 per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
67
68 if (!data && should_create) {
69 auto api_data =
70 base::MakeUnique<APIEventPerContextData>(context->GetIsolate());
71 data = api_data.get();
72 per_context_data->SetUserData(kExtensionAPIEventPerContextKey,
73 std::move(api_data));
74 }
75
76 return data;
77 }
78
79 void DispatchEvent(const v8::FunctionCallbackInfo<v8::Value>& info) {
80 v8::Isolate* isolate = info.GetIsolate();
81 v8::HandleScope handle_scope(isolate);
82 if (info.Length() != 1 || !info[0]->IsArray()) {
83 NOTREACHED();
84 return;
85 }
86
87 v8::Local<v8::Context> context = isolate->GetCurrentContext();
88 APIEventPerContextData* data = GetContextData(context, false);
89 DCHECK(data);
90 std::string event_name = gin::V8ToString(info.Data());
91 auto iter = data->emitters.find(event_name);
92 if (iter == data->emitters.end())
93 return;
94 v8::Global<v8::Object>& v8_emitter = iter->second;
95
96 std::vector<v8::Local<v8::Value>> args;
97 CHECK(gin::Converter<std::vector<v8::Local<v8::Value>>>::FromV8(
98 isolate, info[0], &args));
99
100 EventEmitter* emitter = nullptr;
101 gin::Converter<EventEmitter*>::FromV8(isolate, v8_emitter.Get(isolate),
102 &emitter);
103 CHECK(emitter);
104 emitter->Fire(context, &args, nullptr);
105 }
106
107 } // namespace
108
109 APIEventHandler::APIEventHandler(
110 const binding::RunJSFunction& call_js,
111 const binding::RunJSFunctionSync& call_js_sync,
112 const EventListenersChangedMethod& listeners_changed)
113 : call_js_(call_js),
114 call_js_sync_(call_js_sync),
115 listeners_changed_(listeners_changed) {}
116 APIEventHandler::~APIEventHandler() {}
117
118 v8::Local<v8::Object> APIEventHandler::CreateEventInstance(
119 const std::string& event_name,
120 bool supports_filters,
121 int max_listeners,
122 bool notify_on_change,
123 v8::Local<v8::Context> context) {
124 // We need a context scope since gin::CreateHandle only takes the isolate
125 // and infers the context from that.
126 // TODO(devlin): This could be avoided if gin::CreateHandle could take a
127 // context directly.
128 v8::Context::Scope context_scope(context);
129
130 APIEventPerContextData* data = GetContextData(context, true);
131 DCHECK(data->emitters.find(event_name) == data->emitters.end());
132
133 APIEventListeners::ListenersUpdated updated =
134 notify_on_change ? base::Bind(listeners_changed_, event_name)
135 : base::Bind(&DoNothingOnListenersChanged);
136 std::unique_ptr<APIEventListeners> listeners;
137 if (supports_filters) {
138 listeners = base::MakeUnique<FilteredEventListeners>(
139 updated, event_name, max_listeners, &event_filter_);
140 } else {
141 listeners =
142 base::MakeUnique<UnfilteredEventListeners>(updated, max_listeners);
143 }
144
145 gin::Handle<EventEmitter> emitter_handle =
146 gin::CreateHandle(context->GetIsolate(),
147 new EventEmitter(supports_filters, std::move(listeners),
148 call_js_, call_js_sync_));
149 CHECK(!emitter_handle.IsEmpty());
150 v8::Local<v8::Value> emitter_value = emitter_handle.ToV8();
151 CHECK(emitter_value->IsObject());
152 v8::Local<v8::Object> emitter_object =
153 v8::Local<v8::Object>::Cast(emitter_value);
154 data->emitters[event_name] =
155 v8::Global<v8::Object>(context->GetIsolate(), emitter_object);
156
157 return emitter_object;
158 }
159
160 v8::Local<v8::Object> APIEventHandler::CreateAnonymousEventInstance(
161 v8::Local<v8::Context> context) {
162 v8::Context::Scope context_scope(context);
163 APIEventPerContextData* data = GetContextData(context, true);
164 bool supports_filters = false;
165 std::unique_ptr<APIEventListeners> listeners =
166 base::MakeUnique<UnfilteredEventListeners>(
167 base::Bind(&DoNothingOnListenersChanged), binding::kNoListenerMax);
168 gin::Handle<EventEmitter> emitter_handle =
169 gin::CreateHandle(context->GetIsolate(),
170 new EventEmitter(supports_filters, std::move(listeners),
171 call_js_, call_js_sync_));
172 CHECK(!emitter_handle.IsEmpty());
173 v8::Local<v8::Object> emitter_object = emitter_handle.ToV8().As<v8::Object>();
174 data->anonymous_emitters.push_back(
175 v8::Global<v8::Object>(context->GetIsolate(), emitter_object));
176 return emitter_object;
177 }
178
179 void APIEventHandler::InvalidateCustomEvent(v8::Local<v8::Context> context,
180 v8::Local<v8::Object> event) {
181 EventEmitter* emitter = nullptr;
182 APIEventPerContextData* data = GetContextData(context, false);
183 if (!data || !gin::Converter<EventEmitter*>::FromV8(context->GetIsolate(),
184 event, &emitter)) {
185 NOTREACHED();
186 return;
187 }
188
189 emitter->Invalidate(context);
190 auto emitter_entry = std::find(data->anonymous_emitters.begin(),
191 data->anonymous_emitters.end(), event);
192 if (emitter_entry == data->anonymous_emitters.end()) {
193 NOTREACHED();
194 return;
195 }
196
197 data->anonymous_emitters.erase(emitter_entry);
198 }
199
200 void APIEventHandler::FireEventInContext(const std::string& event_name,
201 v8::Local<v8::Context> context,
202 const base::ListValue& args,
203 const EventFilteringInfo& filter) {
204 APIEventPerContextData* data = GetContextData(context, false);
205 if (!data)
206 return;
207
208 auto emitter_iter = data->emitters.find(event_name);
209 if (emitter_iter == data->emitters.end())
210 return;
211
212 EventEmitter* emitter = nullptr;
213 gin::Converter<EventEmitter*>::FromV8(
214 context->GetIsolate(), emitter_iter->second.Get(context->GetIsolate()),
215 &emitter);
216 CHECK(emitter);
217
218 if (emitter->GetNumListeners() == 0u)
219 return;
220
221 // Note: since we only convert the arguments once, if a listener modifies an
222 // object (including an array), other listeners will see that modification.
223 // TODO(devlin): This is how it's always been, but should it be?
224 std::unique_ptr<content::V8ValueConverter> converter =
225 content::V8ValueConverter::Create();
226
227 auto massager_iter = data->massagers.find(event_name);
228 if (massager_iter == data->massagers.end()) {
229 std::vector<v8::Local<v8::Value>> v8_args;
230 v8_args.reserve(args.GetSize());
231 for (const auto& arg : args)
232 v8_args.push_back(converter->ToV8Value(&arg, context));
233 emitter->Fire(context, &v8_args, &filter);
234 } else {
235 v8::Isolate* isolate = context->GetIsolate();
236 v8::HandleScope handle_scope(isolate);
237 v8::Local<v8::Function> massager = massager_iter->second.Get(isolate);
238 v8::Local<v8::Value> v8_args = converter->ToV8Value(&args, context);
239 DCHECK(!v8_args.IsEmpty());
240 DCHECK(v8_args->IsArray());
241
242 // Curry in the native dispatch function. Some argument massagers take
243 // extra liberties and call this asynchronously, so we can't just have the
244 // massager return a modified array of arguments.
245 // We don't store this in a template because the Data (event name) is
246 // different for each instance. Luckily, this is called during dispatching
247 // an event, rather than e.g. at initialization time.
248 v8::Local<v8::Function> dispatch_event = v8::Function::New(
249 isolate, &DispatchEvent, gin::StringToSymbol(isolate, event_name));
250
251 v8::Local<v8::Value> massager_args[] = {v8_args, dispatch_event};
252 call_js_.Run(massager, context, arraysize(massager_args), massager_args);
253 }
254 }
255
256 void APIEventHandler::RegisterArgumentMassager(
257 v8::Local<v8::Context> context,
258 const std::string& event_name,
259 v8::Local<v8::Function> massager) {
260 APIEventPerContextData* data = GetContextData(context, true);
261 DCHECK(data->massagers.find(event_name) == data->massagers.end());
262 data->massagers[event_name].Reset(context->GetIsolate(), massager);
263 }
264
265 bool APIEventHandler::HasListenerForEvent(const std::string& event_name,
266 v8::Local<v8::Context> context) {
267 APIEventPerContextData* data = GetContextData(context, false);
268 if (!data)
269 return false;
270
271 auto iter = data->emitters.find(event_name);
272 if (iter == data->emitters.end())
273 return false;
274 EventEmitter* emitter = nullptr;
275 gin::Converter<EventEmitter*>::FromV8(
276 context->GetIsolate(), iter->second.Get(context->GetIsolate()), &emitter);
277 CHECK(emitter);
278 return emitter->GetNumListeners() > 0;
279 }
280
281 void APIEventHandler::InvalidateContext(v8::Local<v8::Context> context) {
282 gin::PerContextData* per_context_data = gin::PerContextData::From(context);
283 DCHECK(per_context_data);
284 APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
285 per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
286 if (!data)
287 return;
288
289 v8::Isolate* isolate = context->GetIsolate();
290 v8::HandleScope scope(isolate);
291
292 // This loop *shouldn't* allow any self-modification (i.e., no listeners
293 // should be added or removed as a result of the iteration). If that changes,
294 // we'll need to cache the listeners elsewhere before iterating.
295 for (const auto& pair : data->emitters) {
296 EventEmitter* emitter = nullptr;
297 gin::Converter<EventEmitter*>::FromV8(isolate, pair.second.Get(isolate),
298 &emitter);
299 CHECK(emitter);
300 emitter->Invalidate(context);
301 }
302 for (const auto& global : data->anonymous_emitters) {
303 EventEmitter* emitter = nullptr;
304 gin::Converter<EventEmitter*>::FromV8(isolate, global.Get(isolate),
305 &emitter);
306 CHECK(emitter);
307 emitter->Invalidate(context);
308 }
309
310 data->emitters.clear();
311 data->massagers.clear();
312 data->anonymous_emitters.clear();
313
314 // InvalidateContext() is called shortly (and, theoretically, synchronously)
315 // before the PerContextData is deleted. We have a check that guarantees that
316 // no new EventEmitters are created after the PerContextData is deleted, so
317 // no new emitters should be created after this point.
318 }
319
320 size_t APIEventHandler::GetNumEventListenersForTesting(
321 const std::string& event_name,
322 v8::Local<v8::Context> context) {
323 APIEventPerContextData* data = GetContextData(context, false);
324 DCHECK(data);
325
326 auto iter = data->emitters.find(event_name);
327 DCHECK(iter != data->emitters.end());
328 EventEmitter* emitter = nullptr;
329 gin::Converter<EventEmitter*>::FromV8(
330 context->GetIsolate(), iter->second.Get(context->GetIsolate()), &emitter);
331 CHECK(emitter);
332 return emitter->GetNumListeners();
333 }
334
335 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/api_event_handler.h ('k') | extensions/renderer/api_event_handler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698