| Index: extensions/renderer/api_event_handler.cc
|
| diff --git a/extensions/renderer/api_event_handler.cc b/extensions/renderer/api_event_handler.cc
|
| index 2391607e67ab5955be1ae634c9df4379cd6c6252..d05e9e48c40ad0275f216647b920dc166eb5f1f4 100644
|
| --- a/extensions/renderer/api_event_handler.cc
|
| +++ b/extensions/renderer/api_event_handler.cc
|
| @@ -33,7 +33,7 @@ struct APIEventPerContextData : public base::SupportsUserData::Data {
|
| // v8 objects. This helps us avoid cycles in v8 where an event listener
|
| // could hold a reference to the event, which in turn holds the reference
|
| // to the listener.
|
| - for (const auto& pair : event_data) {
|
| + for (const auto& pair : emitters) {
|
| EventEmitter* emitter = nullptr;
|
| gin::Converter<EventEmitter*>::FromV8(
|
| isolate, pair.second.Get(isolate), &emitter);
|
| @@ -47,9 +47,59 @@ struct APIEventPerContextData : public base::SupportsUserData::Data {
|
| v8::Isolate* isolate;
|
|
|
| // A map from event name -> event emitter.
|
| - std::map<std::string, v8::Global<v8::Object>> event_data;
|
| + std::map<std::string, v8::Global<v8::Object>> emitters;
|
| +
|
| + // A map from event name -> argument massager.
|
| + std::map<std::string, v8::Global<v8::Function>> massagers;
|
| };
|
|
|
| +APIEventPerContextData* GetContextData(v8::Local<v8::Context> context,
|
| + bool should_create) {
|
| + gin::PerContextData* per_context_data = gin::PerContextData::From(context);
|
| + if (!per_context_data)
|
| + return nullptr;
|
| + auto* data = static_cast<APIEventPerContextData*>(
|
| + per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
|
| +
|
| + if (!data && should_create) {
|
| + auto api_data =
|
| + base::MakeUnique<APIEventPerContextData>(context->GetIsolate());
|
| + data = api_data.get();
|
| + per_context_data->SetUserData(kExtensionAPIEventPerContextKey,
|
| + api_data.release());
|
| + }
|
| +
|
| + return data;
|
| +}
|
| +
|
| +void DispatchEvent(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
| + v8::Isolate* isolate = info.GetIsolate();
|
| + v8::HandleScope handle_scope(isolate);
|
| + if (info.Length() != 1 || !info[0]->IsArray()) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +
|
| + v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
| + APIEventPerContextData* data = GetContextData(context, false);
|
| + DCHECK(data);
|
| + std::string event_name = gin::V8ToString(info.Data());
|
| + auto iter = data->emitters.find(event_name);
|
| + if (iter == data->emitters.end())
|
| + return;
|
| + v8::Global<v8::Object>& v8_emitter = iter->second;
|
| +
|
| + std::vector<v8::Local<v8::Value>> args;
|
| + CHECK(gin::Converter<std::vector<v8::Local<v8::Value>>>::FromV8(
|
| + isolate, info[0], &args));
|
| +
|
| + EventEmitter* emitter = nullptr;
|
| + gin::Converter<EventEmitter*>::FromV8(isolate, v8_emitter.Get(isolate),
|
| + &emitter);
|
| + CHECK(emitter);
|
| + emitter->Fire(context, &args);
|
| +}
|
| +
|
| } // namespace
|
|
|
| APIEventHandler::APIEventHandler(
|
| @@ -67,19 +117,8 @@ v8::Local<v8::Object> APIEventHandler::CreateEventInstance(
|
| // context directly.
|
| v8::Context::Scope context_scope(context);
|
|
|
| - gin::PerContextData* per_context_data = gin::PerContextData::From(context);
|
| - DCHECK(per_context_data);
|
| - APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
|
| - per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
|
| - if (!data) {
|
| - auto api_data =
|
| - base::MakeUnique<APIEventPerContextData>(context->GetIsolate());
|
| - data = api_data.get();
|
| - per_context_data->SetUserData(kExtensionAPIEventPerContextKey,
|
| - api_data.release());
|
| - }
|
| -
|
| - DCHECK(data->event_data.find(event_name) == data->event_data.end());
|
| + APIEventPerContextData* data = GetContextData(context, true);
|
| + DCHECK(data->emitters.find(event_name) == data->emitters.end());
|
|
|
| gin::Handle<EventEmitter> emitter_handle = gin::CreateHandle(
|
| context->GetIsolate(),
|
| @@ -89,7 +128,7 @@ v8::Local<v8::Object> APIEventHandler::CreateEventInstance(
|
| CHECK(emitter_value->IsObject());
|
| v8::Local<v8::Object> emitter_object =
|
| v8::Local<v8::Object>::Cast(emitter_value);
|
| - data->event_data[event_name] =
|
| + data->emitters[event_name] =
|
| v8::Global<v8::Object>(context->GetIsolate(), emitter_object);
|
|
|
| return emitter_object;
|
| @@ -98,46 +137,75 @@ v8::Local<v8::Object> APIEventHandler::CreateEventInstance(
|
| void APIEventHandler::FireEventInContext(const std::string& event_name,
|
| v8::Local<v8::Context> context,
|
| const base::ListValue& args) {
|
| - gin::PerContextData* per_context_data = gin::PerContextData::From(context);
|
| - DCHECK(per_context_data);
|
| - APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
|
| - per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
|
| + APIEventPerContextData* data = GetContextData(context, false);
|
| if (!data)
|
| return;
|
|
|
| - auto iter = data->event_data.find(event_name);
|
| - if (iter == data->event_data.end())
|
| + auto emitter_iter = data->emitters.find(event_name);
|
| + if (emitter_iter == data->emitters.end())
|
| + return;
|
| +
|
| + EventEmitter* emitter = nullptr;
|
| + gin::Converter<EventEmitter*>::FromV8(
|
| + context->GetIsolate(), emitter_iter->second.Get(context->GetIsolate()),
|
| + &emitter);
|
| + CHECK(emitter);
|
| +
|
| + if (emitter->listeners()->empty())
|
| return;
|
|
|
| // Note: since we only convert the arguments once, if a listener modifies an
|
| // object (including an array), other listeners will see that modification.
|
| // TODO(devlin): This is how it's always been, but should it be?
|
| - std::vector<v8::Local<v8::Value>> v8_args;
|
| - v8_args.reserve(args.GetSize());
|
| std::unique_ptr<content::V8ValueConverter> converter(
|
| content::V8ValueConverter::create());
|
| - for (const auto& arg : args)
|
| - v8_args.push_back(converter->ToV8Value(arg.get(), context));
|
|
|
| - EventEmitter* emitter = nullptr;
|
| - gin::Converter<EventEmitter*>::FromV8(
|
| - context->GetIsolate(), iter->second.Get(context->GetIsolate()), &emitter);
|
| - CHECK(emitter);
|
| + auto massager_iter = data->massagers.find(event_name);
|
| + if (massager_iter == data->massagers.end()) {
|
| + std::vector<v8::Local<v8::Value>> v8_args;
|
| + v8_args.reserve(args.GetSize());
|
| + for (const auto& arg : args)
|
| + v8_args.push_back(converter->ToV8Value(arg.get(), context));
|
| + emitter->Fire(context, &v8_args);
|
| + } else {
|
| + v8::Isolate* isolate = context->GetIsolate();
|
| + v8::HandleScope handle_scope(isolate);
|
| + v8::Local<v8::Function> massager = massager_iter->second.Get(isolate);
|
| + v8::Local<v8::Value> v8_args = converter->ToV8Value(&args, context);
|
| + DCHECK(!v8_args.IsEmpty());
|
| + DCHECK(v8_args->IsArray());
|
| +
|
| + // Curry in the native dispatch function. Some argument massagers take
|
| + // extra liberties and call this asynchronously, so we can't just have the
|
| + // massager return a modified array of arguments.
|
| + // We don't store this in a template because the Data (event name) is
|
| + // different for each instance. Luckily, this is called during dispatching
|
| + // an event, rather than e.g. at initialization time.
|
| + v8::Local<v8::Function> dispatch_event = v8::Function::New(
|
| + isolate, &DispatchEvent, gin::StringToSymbol(isolate, event_name));
|
| +
|
| + v8::Local<v8::Value> massager_args[] = {v8_args, dispatch_event};
|
| + call_js_.Run(massager, context, arraysize(massager_args), massager_args);
|
| + }
|
| +}
|
|
|
| - emitter->Fire(context, &v8_args);
|
| +void APIEventHandler::RegisterArgumentMassager(
|
| + v8::Local<v8::Context> context,
|
| + const std::string& event_name,
|
| + v8::Local<v8::Function> massager) {
|
| + APIEventPerContextData* data = GetContextData(context, true);
|
| + DCHECK(data->massagers.find(event_name) == data->massagers.end());
|
| + data->massagers[event_name].Reset(context->GetIsolate(), massager);
|
| }
|
|
|
| size_t APIEventHandler::GetNumEventListenersForTesting(
|
| const std::string& event_name,
|
| v8::Local<v8::Context> context) {
|
| - gin::PerContextData* per_context_data = gin::PerContextData::From(context);
|
| - DCHECK(per_context_data);
|
| - APIEventPerContextData* data = static_cast<APIEventPerContextData*>(
|
| - per_context_data->GetUserData(kExtensionAPIEventPerContextKey));
|
| + APIEventPerContextData* data = GetContextData(context, false);
|
| DCHECK(data);
|
|
|
| - auto iter = data->event_data.find(event_name);
|
| - DCHECK(iter != data->event_data.end());
|
| + auto iter = data->emitters.find(event_name);
|
| + DCHECK(iter != data->emitters.end());
|
| EventEmitter* emitter = nullptr;
|
| gin::Converter<EventEmitter*>::FromV8(
|
| context->GetIsolate(), iter->second.Get(context->GetIsolate()), &emitter);
|
|
|