| OLD | NEW |
| (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 | |
| OLD | NEW |