Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 |
| OLD | NEW |