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

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

Issue 2727583004: [Extensions Bindings] Add a registerEventArgumentMassager (Closed)
Patch Set: . 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
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "extensions/renderer/api_event_handler.h" 5 #include "extensions/renderer/api_event_handler.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <map> 8 #include <map>
9 #include <memory> 9 #include <memory>
10 #include <vector> 10 #include <vector>
(...skipping 15 matching lines...) Expand all
26 const char kExtensionAPIEventPerContextKey[] = "extension_api_events"; 26 const char kExtensionAPIEventPerContextKey[] = "extension_api_events";
27 27
28 struct APIEventPerContextData : public base::SupportsUserData::Data { 28 struct APIEventPerContextData : public base::SupportsUserData::Data {
29 APIEventPerContextData(v8::Isolate* isolate) : isolate(isolate) {} 29 APIEventPerContextData(v8::Isolate* isolate) : isolate(isolate) {}
30 ~APIEventPerContextData() override { 30 ~APIEventPerContextData() override {
31 v8::HandleScope scope(isolate); 31 v8::HandleScope scope(isolate);
32 // We explicitly clear the event data map here to remove all references to 32 // We explicitly clear the event data map here to remove all references to
33 // v8 objects. This helps us avoid cycles in v8 where an event listener 33 // v8 objects. This helps us avoid cycles in v8 where an event listener
34 // could hold a reference to the event, which in turn holds the reference 34 // could hold a reference to the event, which in turn holds the reference
35 // to the listener. 35 // to the listener.
36 for (const auto& pair : event_data) { 36 for (const auto& pair : emitters) {
37 EventEmitter* emitter = nullptr; 37 EventEmitter* emitter = nullptr;
38 gin::Converter<EventEmitter*>::FromV8( 38 gin::Converter<EventEmitter*>::FromV8(
39 isolate, pair.second.Get(isolate), &emitter); 39 isolate, pair.second.Get(isolate), &emitter);
40 CHECK(emitter); 40 CHECK(emitter);
41 emitter->listeners()->clear(); 41 emitter->listeners()->clear();
42 } 42 }
43 } 43 }
44 44
45 // The associated v8::Isolate. Since this object is cleaned up at context 45 // The associated v8::Isolate. Since this object is cleaned up at context
46 // destruction, this should always be valid. 46 // destruction, this should always be valid.
47 v8::Isolate* isolate; 47 v8::Isolate* isolate;
48 48
49 // A map from event name -> event emitter. 49 // A map from event name -> event emitter.
50 std::map<std::string, v8::Global<v8::Object>> event_data; 50 std::map<std::string, v8::Global<v8::Object>> emitters;
51
52 // A map from event name -> argument massager.
53 std::map<std::string, v8::Global<v8::Function>> massagers;
51 }; 54 };
52 55
56 APIEventPerContextData* GetContextData(v8::Local<v8::Context> context,
57 bool should_create) {
58 gin::PerContextData* per_context_data = gin::PerContextData::From(context);
59 if (!per_context_data)
60 return nullptr;
61 auto* data = static_cast<APIEventPerContextData*>(
62 per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
63
64 if (!data && should_create) {
65 auto api_data =
66 base::MakeUnique<APIEventPerContextData>(context->GetIsolate());
67 data = api_data.get();
68 per_context_data->SetUserData(kExtensionAPIEventPerContextKey,
69 api_data.release());
70 }
71
72 return data;
73 }
74
75 void DispatchEvent(const v8::FunctionCallbackInfo<v8::Value>& info) {
76 v8::Isolate* isolate = info.GetIsolate();
77 v8::HandleScope handle_scope(isolate);
78 if (info.Length() != 1 || !info[0]->IsArray()) {
79 NOTREACHED();
80 return;
81 }
82
83 v8::Local<v8::Context> context = isolate->GetCurrentContext();
84 APIEventPerContextData* data = GetContextData(context, false);
85 DCHECK(data);
86 std::string event_name = gin::V8ToString(info.Data());
87 auto iter = data->emitters.find(event_name);
88 if (iter == data->emitters.end())
89 return;
90 v8::Global<v8::Object>& v8_emitter = iter->second;
91
92 std::vector<v8::Local<v8::Value>> args;
93 CHECK(gin::Converter<std::vector<v8::Local<v8::Value>>>::FromV8(
94 isolate, info[0], &args));
95
96 EventEmitter* emitter = nullptr;
97 gin::Converter<EventEmitter*>::FromV8(isolate, v8_emitter.Get(isolate),
98 &emitter);
99 CHECK(emitter);
100 emitter->Fire(context, &args);
101 }
102
53 } // namespace 103 } // namespace
54 104
55 APIEventHandler::APIEventHandler( 105 APIEventHandler::APIEventHandler(
56 const binding::RunJSFunction& call_js, 106 const binding::RunJSFunction& call_js,
57 const EventListenersChangedMethod& listeners_changed) 107 const EventListenersChangedMethod& listeners_changed)
58 : call_js_(call_js), listeners_changed_(listeners_changed) {} 108 : call_js_(call_js), listeners_changed_(listeners_changed) {}
59 APIEventHandler::~APIEventHandler() {} 109 APIEventHandler::~APIEventHandler() {}
60 110
61 v8::Local<v8::Object> APIEventHandler::CreateEventInstance( 111 v8::Local<v8::Object> APIEventHandler::CreateEventInstance(
62 const std::string& event_name, 112 const std::string& event_name,
63 v8::Local<v8::Context> context) { 113 v8::Local<v8::Context> context) {
64 // We need a context scope since gin::CreateHandle only takes the isolate 114 // We need a context scope since gin::CreateHandle only takes the isolate
65 // and infers the context from that. 115 // and infers the context from that.
66 // TODO(devlin): This could be avoided if gin::CreateHandle could take a 116 // TODO(devlin): This could be avoided if gin::CreateHandle could take a
67 // context directly. 117 // context directly.
68 v8::Context::Scope context_scope(context); 118 v8::Context::Scope context_scope(context);
69 119
70 gin::PerContextData* per_context_data = gin::PerContextData::From(context); 120 APIEventPerContextData* data = GetContextData(context, true);
71 DCHECK(per_context_data); 121 DCHECK(data->emitters.find(event_name) == data->emitters.end());
72 APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
73 per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
74 if (!data) {
75 auto api_data =
76 base::MakeUnique<APIEventPerContextData>(context->GetIsolate());
77 data = api_data.get();
78 per_context_data->SetUserData(kExtensionAPIEventPerContextKey,
79 api_data.release());
80 }
81
82 DCHECK(data->event_data.find(event_name) == data->event_data.end());
83 122
84 gin::Handle<EventEmitter> emitter_handle = gin::CreateHandle( 123 gin::Handle<EventEmitter> emitter_handle = gin::CreateHandle(
85 context->GetIsolate(), 124 context->GetIsolate(),
86 new EventEmitter(call_js_, base::Bind(listeners_changed_, event_name))); 125 new EventEmitter(call_js_, base::Bind(listeners_changed_, event_name)));
87 CHECK(!emitter_handle.IsEmpty()); 126 CHECK(!emitter_handle.IsEmpty());
88 v8::Local<v8::Value> emitter_value = emitter_handle.ToV8(); 127 v8::Local<v8::Value> emitter_value = emitter_handle.ToV8();
89 CHECK(emitter_value->IsObject()); 128 CHECK(emitter_value->IsObject());
90 v8::Local<v8::Object> emitter_object = 129 v8::Local<v8::Object> emitter_object =
91 v8::Local<v8::Object>::Cast(emitter_value); 130 v8::Local<v8::Object>::Cast(emitter_value);
92 data->event_data[event_name] = 131 data->emitters[event_name] =
93 v8::Global<v8::Object>(context->GetIsolate(), emitter_object); 132 v8::Global<v8::Object>(context->GetIsolate(), emitter_object);
94 133
95 return emitter_object; 134 return emitter_object;
96 } 135 }
97 136
98 void APIEventHandler::FireEventInContext(const std::string& event_name, 137 void APIEventHandler::FireEventInContext(const std::string& event_name,
99 v8::Local<v8::Context> context, 138 v8::Local<v8::Context> context,
100 const base::ListValue& args) { 139 const base::ListValue& args) {
101 gin::PerContextData* per_context_data = gin::PerContextData::From(context); 140 APIEventPerContextData* data = GetContextData(context, false);
102 DCHECK(per_context_data);
103 APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
104 per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
105 if (!data) 141 if (!data)
106 return; 142 return;
107 143
108 auto iter = data->event_data.find(event_name); 144 auto emitter_iter = data->emitters.find(event_name);
109 if (iter == data->event_data.end()) 145 if (emitter_iter == data->emitters.end())
146 return;
147
148 EventEmitter* emitter = nullptr;
149 gin::Converter<EventEmitter*>::FromV8(
150 context->GetIsolate(), emitter_iter->second.Get(context->GetIsolate()),
151 &emitter);
152 CHECK(emitter);
153
154 if (emitter->listeners()->empty())
110 return; 155 return;
111 156
112 // Note: since we only convert the arguments once, if a listener modifies an 157 // Note: since we only convert the arguments once, if a listener modifies an
113 // object (including an array), other listeners will see that modification. 158 // object (including an array), other listeners will see that modification.
114 // TODO(devlin): This is how it's always been, but should it be? 159 // TODO(devlin): This is how it's always been, but should it be?
115 std::vector<v8::Local<v8::Value>> v8_args;
116 v8_args.reserve(args.GetSize());
117 std::unique_ptr<content::V8ValueConverter> converter( 160 std::unique_ptr<content::V8ValueConverter> converter(
118 content::V8ValueConverter::create()); 161 content::V8ValueConverter::create());
119 for (const auto& arg : args)
120 v8_args.push_back(converter->ToV8Value(arg.get(), context));
121 162
122 EventEmitter* emitter = nullptr; 163 auto massager_iter = data->massagers.find(event_name);
123 gin::Converter<EventEmitter*>::FromV8( 164 if (massager_iter == data->massagers.end()) {
124 context->GetIsolate(), iter->second.Get(context->GetIsolate()), &emitter); 165 std::vector<v8::Local<v8::Value>> v8_args;
125 CHECK(emitter); 166 v8_args.reserve(args.GetSize());
167 for (const auto& arg : args)
168 v8_args.push_back(converter->ToV8Value(arg.get(), context));
169 emitter->Fire(context, &v8_args);
170 } else {
171 v8::Isolate* isolate = context->GetIsolate();
172 v8::HandleScope handle_scope(isolate);
173 v8::Local<v8::Function> massager = massager_iter->second.Get(isolate);
174 v8::Local<v8::Value> v8_args = converter->ToV8Value(&args, context);
175 DCHECK(!v8_args.IsEmpty());
176 DCHECK(v8_args->IsArray());
126 177
127 emitter->Fire(context, &v8_args); 178 // Curry in the native dispatch function. Some argument massagers take
179 // extra liberties and call this asynchronously, so we can't just have the
180 // massager return a modified array of arguments.
181 // We don't store this in a template because the Data (event name) is
182 // different for each instance. Luckily, this is called during dispatching
183 // an event, rather than e.g. at initialization time.
184 v8::Local<v8::Function> dispatch_event = v8::Function::New(
185 isolate, &DispatchEvent, gin::StringToSymbol(isolate, event_name));
186
187 v8::Local<v8::Value> massager_args[] = {v8_args, dispatch_event};
188 call_js_.Run(massager, context, arraysize(massager_args), massager_args);
189 }
190 }
191
192 void APIEventHandler::RegisterArgumentMassager(
193 v8::Local<v8::Context> context,
194 const std::string& event_name,
195 v8::Local<v8::Function> massager) {
196 APIEventPerContextData* data = GetContextData(context, true);
197 DCHECK(data->massagers.find(event_name) == data->massagers.end());
198 data->massagers[event_name] =
jbroman 2017/03/01 22:33:35 nit: could use this if you prefer (slightly shorte
Devlin 2017/03/02 17:19:26 Sure, done. I sometimes wonder which is faster un
jbroman 2017/03/02 21:12:40 From a language perspective, the compiler is requi
199 v8::Global<v8::Function>(context->GetIsolate(), massager);
128 } 200 }
129 201
130 size_t APIEventHandler::GetNumEventListenersForTesting( 202 size_t APIEventHandler::GetNumEventListenersForTesting(
131 const std::string& event_name, 203 const std::string& event_name,
132 v8::Local<v8::Context> context) { 204 v8::Local<v8::Context> context) {
133 gin::PerContextData* per_context_data = gin::PerContextData::From(context); 205 APIEventPerContextData* data = GetContextData(context, false);
134 DCHECK(per_context_data);
135 APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
136 per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
137 DCHECK(data); 206 DCHECK(data);
138 207
139 auto iter = data->event_data.find(event_name); 208 auto iter = data->emitters.find(event_name);
140 DCHECK(iter != data->event_data.end()); 209 DCHECK(iter != data->emitters.end());
141 EventEmitter* emitter = nullptr; 210 EventEmitter* emitter = nullptr;
142 gin::Converter<EventEmitter*>::FromV8( 211 gin::Converter<EventEmitter*>::FromV8(
143 context->GetIsolate(), iter->second.Get(context->GetIsolate()), &emitter); 212 context->GetIsolate(), iter->second.Get(context->GetIsolate()), &emitter);
144 CHECK(emitter); 213 CHECK(emitter);
145 return emitter->listeners()->size(); 214 return emitter->listeners()->size();
146 } 215 }
147 216
148 } // namespace extensions 217 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698