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].Reset(context->GetIsolate(), massager); |
128 } | 199 } |
129 | 200 |
130 size_t APIEventHandler::GetNumEventListenersForTesting( | 201 size_t APIEventHandler::GetNumEventListenersForTesting( |
131 const std::string& event_name, | 202 const std::string& event_name, |
132 v8::Local<v8::Context> context) { | 203 v8::Local<v8::Context> context) { |
133 gin::PerContextData* per_context_data = gin::PerContextData::From(context); | 204 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); | 205 DCHECK(data); |
138 | 206 |
139 auto iter = data->event_data.find(event_name); | 207 auto iter = data->emitters.find(event_name); |
140 DCHECK(iter != data->event_data.end()); | 208 DCHECK(iter != data->emitters.end()); |
141 EventEmitter* emitter = nullptr; | 209 EventEmitter* emitter = nullptr; |
142 gin::Converter<EventEmitter*>::FromV8( | 210 gin::Converter<EventEmitter*>::FromV8( |
143 context->GetIsolate(), iter->second.Get(context->GetIsolate()), &emitter); | 211 context->GetIsolate(), iter->second.Get(context->GetIsolate()), &emitter); |
144 CHECK(emitter); | 212 CHECK(emitter); |
145 return emitter->listeners()->size(); | 213 return emitter->listeners()->size(); |
146 } | 214 } |
147 | 215 |
148 } // namespace extensions | 216 } // namespace extensions |
OLD | NEW |