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/event_emitter.h" | 5 #include "extensions/renderer/event_emitter.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "extensions/renderer/api_event_listeners.h" | 9 #include "extensions/renderer/api_event_listeners.h" |
| 10 #include "gin/data_object_builder.h" | |
| 10 #include "gin/object_template_builder.h" | 11 #include "gin/object_template_builder.h" |
| 11 #include "gin/per_context_data.h" | 12 #include "gin/per_context_data.h" |
| 12 | 13 |
| 13 namespace extensions { | 14 namespace extensions { |
| 14 | 15 |
| 15 gin::WrapperInfo EventEmitter::kWrapperInfo = {gin::kEmbedderNativeGin}; | 16 gin::WrapperInfo EventEmitter::kWrapperInfo = {gin::kEmbedderNativeGin}; |
| 16 | 17 |
| 17 EventEmitter::EventEmitter(bool supports_filters, | 18 EventEmitter::EventEmitter(bool supports_filters, |
| 18 std::unique_ptr<APIEventListeners> listeners, | 19 std::unique_ptr<APIEventListeners> listeners, |
| 19 const binding::RunJSFunction& run_js) | 20 const binding::RunJSFunction& run_js, |
| 21 const binding::RunJSFunctionSync& run_js_sync) | |
| 20 : supports_filters_(supports_filters), | 22 : supports_filters_(supports_filters), |
| 21 listeners_(std::move(listeners)), | 23 listeners_(std::move(listeners)), |
| 22 run_js_(run_js) {} | 24 run_js_(run_js), |
| 25 run_js_sync_(run_js_sync) {} | |
| 23 | 26 |
| 24 EventEmitter::~EventEmitter() {} | 27 EventEmitter::~EventEmitter() {} |
| 25 | 28 |
| 26 gin::ObjectTemplateBuilder EventEmitter::GetObjectTemplateBuilder( | 29 gin::ObjectTemplateBuilder EventEmitter::GetObjectTemplateBuilder( |
| 27 v8::Isolate* isolate) { | 30 v8::Isolate* isolate) { |
| 28 return Wrappable<EventEmitter>::GetObjectTemplateBuilder(isolate) | 31 return Wrappable<EventEmitter>::GetObjectTemplateBuilder(isolate) |
| 29 .SetMethod("addListener", &EventEmitter::AddListener) | 32 .SetMethod("addListener", &EventEmitter::AddListener) |
| 30 .SetMethod("removeListener", &EventEmitter::RemoveListener) | 33 .SetMethod("removeListener", &EventEmitter::RemoveListener) |
| 31 .SetMethod("hasListener", &EventEmitter::HasListener) | 34 .SetMethod("hasListener", &EventEmitter::HasListener) |
| 32 .SetMethod("hasListeners", &EventEmitter::HasListeners) | 35 .SetMethod("hasListeners", &EventEmitter::HasListeners) |
| 33 // The following methods aren't part of the public API, but are used | 36 // The following methods aren't part of the public API, but are used |
| 34 // by our custom bindings and exposed on the public event object. :( | 37 // by our custom bindings and exposed on the public event object. :( |
| 35 // TODO(devlin): Once we convert all custom bindings that use these, | 38 // TODO(devlin): Once we convert all custom bindings that use these, |
| 36 // they can be removed. | 39 // they can be removed. |
| 37 .SetMethod("dispatch", &EventEmitter::Dispatch); | 40 .SetMethod("dispatch", &EventEmitter::Dispatch); |
| 38 } | 41 } |
| 39 | 42 |
| 40 void EventEmitter::Fire(v8::Local<v8::Context> context, | 43 void EventEmitter::Fire(v8::Local<v8::Context> context, |
| 41 std::vector<v8::Local<v8::Value>>* args, | 44 std::vector<v8::Local<v8::Value>>* args, |
| 42 const EventFilteringInfo* filter) { | 45 const EventFilteringInfo* filter) { |
| 43 // Note that |listeners_| can be modified during handling. | 46 bool run_sync = false; |
| 44 std::vector<v8::Local<v8::Function>> listeners = | 47 DispatchImpl(context, args, filter, run_sync, nullptr); |
| 45 listeners_->GetListeners(filter, context); | |
| 46 | |
| 47 for (const auto& listener : listeners) { | |
| 48 v8::TryCatch try_catch(context->GetIsolate()); | |
| 49 // SetVerbose() means the error will still get logged, which is what we | |
| 50 // want. We don't let it bubble up any further to prevent it from being | |
| 51 // surfaced in e.g. JS code that triggered the event. | |
| 52 try_catch.SetVerbose(true); | |
| 53 run_js_.Run(listener, context, args->size(), args->data()); | |
| 54 } | |
| 55 } | 48 } |
| 56 | 49 |
| 57 void EventEmitter::Invalidate(v8::Local<v8::Context> context) { | 50 void EventEmitter::Invalidate(v8::Local<v8::Context> context) { |
| 58 valid_ = false; | 51 valid_ = false; |
| 59 listeners_->Invalidate(context); | 52 listeners_->Invalidate(context); |
| 60 } | 53 } |
| 61 | 54 |
| 62 size_t EventEmitter::GetNumListeners() const { | 55 size_t EventEmitter::GetNumListeners() const { |
| 63 return listeners_->GetNumListeners(); | 56 return listeners_->GetNumListeners(); |
| 64 } | 57 } |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 120 bool EventEmitter::HasListeners() { | 113 bool EventEmitter::HasListeners() { |
| 121 return listeners_->GetNumListeners() != 0; | 114 return listeners_->GetNumListeners() != 0; |
| 122 } | 115 } |
| 123 | 116 |
| 124 void EventEmitter::Dispatch(gin::Arguments* arguments) { | 117 void EventEmitter::Dispatch(gin::Arguments* arguments) { |
| 125 if (!valid_) | 118 if (!valid_) |
| 126 return; | 119 return; |
| 127 | 120 |
| 128 if (listeners_->GetNumListeners() == 0) | 121 if (listeners_->GetNumListeners() == 0) |
| 129 return; | 122 return; |
| 130 v8::HandleScope handle_scope(arguments->isolate()); | 123 |
| 131 v8::Local<v8::Context> context = | 124 v8::Isolate* isolate = arguments->isolate(); |
| 132 arguments->isolate()->GetCurrentContext(); | 125 v8::HandleScope handle_scope(isolate); |
| 126 v8::Local<v8::Context> context = isolate->GetCurrentContext(); | |
| 133 std::vector<v8::Local<v8::Value>> v8_args = arguments->GetAll(); | 127 std::vector<v8::Local<v8::Value>> v8_args = arguments->GetAll(); |
| 134 Fire(context, &v8_args, nullptr); | 128 |
| 129 // Dispatch() is called from JS, and sometimes expects a return value of an | |
| 130 // array with entries for each of the results of the listeners. Since this is | |
| 131 // directly from JS, we know it should be safe to call synchronously and use | |
| 132 // the return result, so we don't use Fire(). | |
| 133 // TODO(devlin): It'd be nice to refactor anything expecting a result here so | |
| 134 // we don't have to have this special logic, especially since script could | |
| 135 // potentially tweak the result object through prototype manipulation (which | |
| 136 // also means we should never use this for security decisions). | |
| 137 bool run_sync = true; | |
| 138 std::vector<v8::Global<v8::Value>> listener_responses; | |
| 139 DispatchImpl(context, &v8_args, nullptr, run_sync, &listener_responses); | |
| 140 | |
| 141 if (!listener_responses.size()) { | |
| 142 // Return nothing if there are no responses. This is the behavior of the | |
| 143 // current JS implementation. | |
| 144 return; | |
| 145 } | |
| 146 | |
| 147 v8::Local<v8::Object> result; | |
| 148 { | |
| 149 v8::TryCatch try_catch(isolate); | |
| 150 try_catch.SetVerbose(true); | |
| 151 v8::Local<v8::Array> v8_responses = | |
| 152 v8::Array::New(isolate, listener_responses.size()); | |
| 153 for (size_t i = 0; i < listener_responses.size(); ++i) { | |
| 154 CHECK(v8_responses | |
| 155 ->CreateDataProperty(context, i, | |
|
jbroman
2017/06/09 19:56:36
Can there ever be more than 2^32 - 2 listeners? If
Devlin
2017/06/09 20:26:21
There shouldn't be, but I've added a TODO to enfor
| |
| 156 listener_responses[i].Get(isolate)) | |
| 157 .ToChecked()); | |
| 158 } | |
| 159 | |
| 160 result = gin::DataObjectBuilder(isolate) | |
| 161 .Set("results", v8_responses.As<v8::Value>()) | |
| 162 .Build(); | |
| 163 } | |
| 164 arguments->Return(result); | |
| 165 } | |
| 166 | |
| 167 void EventEmitter::DispatchImpl( | |
| 168 v8::Local<v8::Context> context, | |
| 169 std::vector<v8::Local<v8::Value>>* args, | |
| 170 const EventFilteringInfo* filter, | |
| 171 bool run_sync, | |
| 172 std::vector<v8::Global<v8::Value>>* out_values) { | |
| 173 // Note that |listeners_| can be modified during handling. | |
| 174 std::vector<v8::Local<v8::Function>> listeners = | |
| 175 listeners_->GetListeners(filter, context); | |
| 176 | |
| 177 v8::Isolate* isolate = context->GetIsolate(); | |
| 178 v8::TryCatch try_catch(isolate); | |
| 179 // SetVerbose() means the error will still get logged, which is what we | |
| 180 // want. We don't let it bubble up any further to prevent it from being | |
| 181 // surfaced in e.g. JS code that triggered the event. | |
| 182 try_catch.SetVerbose(true); | |
| 183 for (const auto& listener : listeners) { | |
| 184 if (run_sync) { | |
| 185 DCHECK(out_values); | |
| 186 v8::Global<v8::Value> result = | |
| 187 run_js_sync_.Run(listener, context, args->size(), args->data()); | |
| 188 if (!result.IsEmpty() && !result.Get(isolate)->IsUndefined()) | |
| 189 out_values->push_back(std::move(result)); | |
| 190 } else { | |
| 191 run_js_.Run(listener, context, args->size(), args->data()); | |
| 192 } | |
| 193 } | |
| 135 } | 194 } |
| 136 | 195 |
| 137 } // namespace extensions | 196 } // namespace extensions |
| OLD | NEW |