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

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

Issue 2469593002: [Extensions Bindings] Add Events support (Closed)
Patch Set: . Created 4 years, 1 month 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
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/values.h"
13 #include "content/public/child/v8_value_converter.h"
14 #include "gin/arguments.h"
15 #include "gin/per_context_data.h"
16
17 namespace extensions {
18
19 namespace {
20
21 const char kExtensionAPIEventPerContextKey[] = "extension_api_events";
22 const char kEventListenersKey[] = "event_listeners";
23
24 void ForwardToGinHandler(const v8::FunctionCallbackInfo<v8::Value>& info) {
25 gin::Arguments args(info);
26 v8::Local<v8::External> external;
27 CHECK(args.GetData(&external));
28 auto callback =
29 static_cast<APIEventHandler::HandlerCallback*>(external->Value());
30 callback->Run(info.This(), &args);
31 }
32
33 } // namespace
34
35 APIEventHandler::APIEventPerContextData::APIEventPerContextData() = default;
36 APIEventHandler::APIEventPerContextData::~APIEventPerContextData() = default;
37
38 APIEventHandler::APIEventHandler(const binding::RunJSFunction& call_js)
39 : call_js_(call_js), weak_factory_(this) {}
40 APIEventHandler::~APIEventHandler() {}
41
42 v8::Local<v8::Object> APIEventHandler::CreateEventInstance(
jbroman 2016/11/01 18:59:24 High-level comment here: while we needed to get cl
jbroman 2016/11/01 19:14:33 One note here: a little more caution than I've imp
Devlin 2016/11/02 00:48:29 So, there's a few gotchas here and there with how
43 const std::string& event_name,
44 v8::Local<v8::Context> context) {
45 gin::PerContextData* per_context_data = gin::PerContextData::From(context);
46 DCHECK(per_context_data);
47 APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
48 per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
49 if (!data) {
50 auto api_data = base::MakeUnique<APIEventPerContextData>();
51 data = api_data.get();
52 per_context_data->SetUserData(kExtensionAPIEventPerContextKey,
53 api_data.release());
54 }
55
56 DCHECK(data->event_data.find(event_name) == data->event_data.end());
57 auto listeners = base::MakeUnique<EventListeners>();
58
59 if (event_template_.IsEmpty())
60 InitializeTemplate(context->GetIsolate(), data);
61
62 v8::Local<v8::ObjectTemplate> local_template =
63 event_template_.Get(context->GetIsolate());
64 v8::MaybeLocal<v8::Object> maybe_object =
65 local_template->NewInstance(context);
66 v8::Local<v8::Object> object;
67 // TODO(devlin): Could this fail?
68 CHECK(maybe_object.ToLocal(&object));
jbroman 2016/11/01 18:59:24 I believe this won't fail, because we know that si
Devlin 2016/11/02 00:48:29 Moot if we go with the latest patch set.
69
70 // We cache the EventListeners pointer on the event object for easy lookups
71 // when adding, removing, or querying listeners from JS.
72 v8::Maybe<bool> success = object->SetPrivate(
73 context, v8::Private::ForApi(context->GetIsolate(),
74 gin::StringToSymbol(context->GetIsolate(),
75 kEventListenersKey)),
76 v8::External::New(context->GetIsolate(), listeners.get()));
77 DCHECK(success.IsJust());
78 DCHECK(success.FromJust());
79
80 data->event_data.insert(std::make_pair(event_name, std::move(listeners)));
81
82 return object;
83 }
84
85 void APIEventHandler::InitializeTemplate(v8::Isolate* isolate,
86 APIEventPerContextData* data) {
87 v8::Local<v8::ObjectTemplate> event_template =
88 v8::ObjectTemplate::New(isolate);
89
90 {
91 struct Method {
92 const char* name;
93 void (APIEventHandler::*handler)(v8::Local<v8::Object> object,
94 gin::Arguments* args);
95 } methods[] = {
96 {"addListener", &APIEventHandler::AddListener},
97 {"removeListener", &APIEventHandler::RemoveListener},
98 {"hasListener", &APIEventHandler::HasListener},
99 {"hasListeners", &APIEventHandler::HasListeners},
100 };
101 for (const auto& method : methods) {
102 auto handler_callback = base::MakeUnique<HandlerCallback>(
103 base::Bind(method.handler, weak_factory_.GetWeakPtr()));
104 // TODO(devlin): FunctionTemplate docs say "There can only be one function
105 // created from a FunctionTemplate in a context." I'm assuming that means
106 // we can't change the template, rather than we can only use it to
107 // instantiate a single function object?
jbroman 2016/11/01 18:59:24 It means that if you call v8::FunctionTemplate::Ge
Devlin 2016/11/02 00:48:29 Ditto
108 v8::Local<v8::FunctionTemplate> function = v8::FunctionTemplate::New(
109 isolate, &ForwardToGinHandler,
110 v8::External::New(isolate, handler_callback.get()),
111 v8::Local<v8::Signature>(), 0, v8::ConstructorBehavior::kThrow);
jbroman 2016/11/01 18:59:24 If you use a signature, you can have V8 validate t
Devlin 2016/11/02 00:48:29 Ditto
112 data->callbacks.push_back(std::move(handler_callback));
113 event_template->Set(gin::StringToSymbol(isolate, method.name), function);
114 }
115 }
116
117 event_template_ = v8::Global<v8::ObjectTemplate>(isolate, event_template);
118 }
119
120 void APIEventHandler::FireEventInContext(const std::string& event_name,
121 v8::Local<v8::Context> context,
122 const base::ListValue& args) {
123 gin::PerContextData* per_context_data = gin::PerContextData::From(context);
124 DCHECK(per_context_data);
125 APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
126 per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
127 if (!data)
128 return;
129
130 auto iter = data->event_data.find(event_name);
131 if (iter == data->event_data.end())
132 return;
133
134 EventListeners* listeners = iter->second.get();
135
136 std::vector<v8::Local<v8::Value>> v8_args;
137 v8_args.reserve(args.GetSize());
138 std::unique_ptr<content::V8ValueConverter> converter(
139 content::V8ValueConverter::create());
140 for (const auto& arg : args)
141 v8_args.push_back(converter->ToV8Value(arg.get(), context));
142
143 for (auto& listener : *listeners) {
144 call_js_.Run(listener.Get(context->GetIsolate()), context, v8_args.size(),
145 v8_args.data());
146 }
147 }
148
149 const APIEventHandler::EventListeners&
150 APIEventHandler::GetEventListenersForTesting(const std::string& event_name,
151 v8::Local<v8::Context> context) {
152 gin::PerContextData* per_context_data = gin::PerContextData::From(context);
153 DCHECK(per_context_data);
154 APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
155 per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
156 DCHECK(data);
157
158 auto iter = data->event_data.find(event_name);
159 DCHECK(iter != data->event_data.end());
160 return *iter->second;
161 }
162
163 APIEventHandler::EventListeners* APIEventHandler::GetListenersForObject(
164 v8::Local<v8::Object> object,
165 v8::Local<v8::Context> context) {
166 v8::MaybeLocal<v8::Value> maybe_value = object->GetPrivate(
167 context, v8::Private::ForApi(context->GetIsolate(),
168 gin::StringToSymbol(context->GetIsolate(),
169 kEventListenersKey)));
170 v8::Local<v8::Value> value;
171 CHECK(maybe_value.ToLocal(&value));
172 CHECK(value->IsExternal());
jbroman 2016/11/01 18:59:24 You can't rely on this working, at the moment. You
Devlin 2016/11/02 00:48:29 Also moot.
173 v8::Local<v8::External> external = v8::Local<v8::External>::Cast(value);
174 return static_cast<EventListeners*>(external->Value());
175 }
176
177 void APIEventHandler::AddListener(v8::Local<v8::Object> caller,
178 gin::Arguments* args) {
179 v8::Local<v8::Function> listener;
180 if (args->Length() != 1 || !args->GetNext(&listener))
181 return;
182 EventListeners* listeners =
183 GetListenersForObject(caller, args->isolate()->GetCurrentContext());
jbroman 2016/11/01 18:59:24 Didn't catch this before, but getting the context
Devlin 2016/11/02 00:48:29 And moot :)
184 if (std::find(listeners->begin(), listeners->end(), listener) !=
185 listeners->end())
186 return;
187 listeners->push_back(v8::Global<v8::Function>(args->isolate(), listener));
188 }
189
190 void APIEventHandler::RemoveListener(v8::Local<v8::Object> caller,
191 gin::Arguments* args) {
192 v8::Local<v8::Function> listener;
193 if (args->Length() != 1 || !args->GetNext(&listener))
194 return;
195 EventListeners* listeners =
196 GetListenersForObject(caller, args->isolate()->GetCurrentContext());
197 auto iter = std::find(listeners->begin(), listeners->end(), listener);
198 if (iter != listeners->end())
199 listeners->erase(iter);
200 }
201
202 void APIEventHandler::HasListener(v8::Local<v8::Object> caller,
203 gin::Arguments* args) {
204 v8::Local<v8::Function> listener;
205 if (args->Length() != 1 || !args->GetNext(&listener))
206 return;
207 EventListeners* listeners =
208 GetListenersForObject(caller, args->isolate()->GetCurrentContext());
209 auto iter = std::find(listeners->begin(), listeners->end(), listener);
210 args->Return(iter != listeners->end());
211 }
212
213 void APIEventHandler::HasListeners(v8::Local<v8::Object> caller,
214 gin::Arguments* args) {
215 if (args->Length() != 0)
216 return;
217 EventListeners* listeners =
218 GetListenersForObject(caller, args->isolate()->GetCurrentContext());
219 args->Return(!listeners->empty());
220 }
221
222 } // 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