Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: extensions/renderer/event_emitter.cc

Issue 2921013002: [Extensions Bindings] Return result from event dispatch (Closed)
Patch Set: . Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « extensions/renderer/event_emitter.h ('k') | extensions/renderer/event_emitter_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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 v8::Local<v8::Object> result;
142 {
143 v8::TryCatch try_catch(isolate);
144 try_catch.SetVerbose(true);
145 v8::Local<v8::Array> v8_responses = v8::Array::New(isolate);
jbroman 2017/06/06 18:29:10 super-nit: length is already known; pass it to v8:
Devlin 2017/06/09 18:20:14 Thanks! Done.
146 for (size_t i = 0; i < listener_responses.size(); ++i) {
147 v8::TryCatch try_catch(isolate);
148 try_catch.SetVerbose(true);
jbroman 2017/06/06 18:29:09 You already have a TryCatch a few lines up, outsid
Devlin 2017/06/09 18:20:14 Done.
149 if (!v8_responses->Set(context, i, listener_responses[i].Get(isolate))
jbroman 2017/06/06 18:29:09 warning: v8::Object::Set can invoke author script,
Devlin 2017/06/09 18:20:14 Done. Also converted to CHECK(CreateDataProperty(
jbroman 2017/06/09 19:56:35 I can't imagine how it could fail; sgtm.
150 .IsJust()) {
151 return;
152 }
153 }
154
155 result = gin::DataObjectBuilder(isolate)
jbroman 2017/06/06 18:29:09 The current code does the slightly quirky thing wh
Devlin 2017/06/09 18:20:14 Semi-intentional, but probably not worth it. Upda
156 .Set("results", v8_responses.As<v8::Value>())
157 .Build();
158 }
159 arguments->Return(result.As<v8::Value>());
jbroman 2017/06/06 18:29:09 nit: is the upcast needed here?
Devlin 2017/06/09 18:20:14 I thought so, but apparently not. Removed.
160 }
161
162 void EventEmitter::DispatchImpl(
163 v8::Local<v8::Context> context,
164 std::vector<v8::Local<v8::Value>>* args,
165 const EventFilteringInfo* filter,
166 bool run_sync,
167 std::vector<v8::Global<v8::Value>>* out_values) {
168 // Note that |listeners_| can be modified during handling.
169 std::vector<v8::Local<v8::Function>> listeners =
170 listeners_->GetListeners(filter, context);
171
172 for (const auto& listener : listeners) {
173 v8::TryCatch try_catch(context->GetIsolate());
jbroman 2017/06/06 18:29:09 nit: Lift the TryCatch outside the loop?
Devlin 2017/06/09 18:20:14 Done. To confirm, can verbose try-catches handle
jbroman 2017/06/09 19:56:35 Yeah, I'm pretty sure they report it at the time t
174 // SetVerbose() means the error will still get logged, which is what we
175 // want. We don't let it bubble up any further to prevent it from being
176 // surfaced in e.g. JS code that triggered the event.
177 try_catch.SetVerbose(true);
178
179 if (run_sync) {
180 DCHECK(out_values);
181 v8::Global<v8::Value> result =
182 run_js_sync_.Run(listener, context, args->size(), args->data());
183 if (!result.IsEmpty())
jbroman 2017/06/06 18:29:09 This seems different from what the JS does. Unless
Devlin 2017/06/09 18:20:14 The check for empty was deliberate, since some lis
184 out_values->push_back(std::move(result));
185 } else {
186 run_js_.Run(listener, context, args->size(), args->data());
187 }
188 }
135 } 189 }
136 190
137 } // namespace extensions 191 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/event_emitter.h ('k') | extensions/renderer/event_emitter_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698