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

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

Issue 2947463002: [Extensions Bindings] Add a bindings/ subdirectory under renderer (Closed)
Patch Set: . Created 3 years, 5 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "extensions/renderer/event_emitter.h"
6
7 #include <algorithm>
8
9 #include "extensions/renderer/api_event_listeners.h"
10 #include "gin/data_object_builder.h"
11 #include "gin/object_template_builder.h"
12 #include "gin/per_context_data.h"
13
14 namespace extensions {
15
16 gin::WrapperInfo EventEmitter::kWrapperInfo = {gin::kEmbedderNativeGin};
17
18 EventEmitter::EventEmitter(bool supports_filters,
19 std::unique_ptr<APIEventListeners> listeners,
20 const binding::RunJSFunction& run_js,
21 const binding::RunJSFunctionSync& run_js_sync)
22 : supports_filters_(supports_filters),
23 listeners_(std::move(listeners)),
24 run_js_(run_js),
25 run_js_sync_(run_js_sync) {}
26
27 EventEmitter::~EventEmitter() {}
28
29 gin::ObjectTemplateBuilder EventEmitter::GetObjectTemplateBuilder(
30 v8::Isolate* isolate) {
31 return Wrappable<EventEmitter>::GetObjectTemplateBuilder(isolate)
32 .SetMethod("addListener", &EventEmitter::AddListener)
33 .SetMethod("removeListener", &EventEmitter::RemoveListener)
34 .SetMethod("hasListener", &EventEmitter::HasListener)
35 .SetMethod("hasListeners", &EventEmitter::HasListeners)
36 // The following methods aren't part of the public API, but are used
37 // by our custom bindings and exposed on the public event object. :(
38 // TODO(devlin): Once we convert all custom bindings that use these,
39 // they can be removed.
40 .SetMethod("dispatch", &EventEmitter::Dispatch);
41 }
42
43 void EventEmitter::Fire(v8::Local<v8::Context> context,
44 std::vector<v8::Local<v8::Value>>* args,
45 const EventFilteringInfo* filter) {
46 bool run_sync = false;
47 DispatchImpl(context, args, filter, run_sync, nullptr);
48 }
49
50 void EventEmitter::Invalidate(v8::Local<v8::Context> context) {
51 valid_ = false;
52 listeners_->Invalidate(context);
53 }
54
55 size_t EventEmitter::GetNumListeners() const {
56 return listeners_->GetNumListeners();
57 }
58
59 void EventEmitter::AddListener(gin::Arguments* arguments) {
60 // If script from another context maintains a reference to this object, it's
61 // possible that functions can be called after this object's owning context
62 // is torn down and released by blink. We don't support this behavior, but
63 // we need to make sure nothing crashes, so early out of methods.
64 if (!valid_)
65 return;
66
67 v8::Local<v8::Function> listener;
68 // TODO(devlin): For some reason, we don't throw an error when someone calls
69 // add/removeListener with no argument. We probably should. For now, keep
70 // the status quo, but we should revisit this.
71 if (!arguments->GetNext(&listener))
72 return;
73
74 if (!arguments->PeekNext().IsEmpty() && !supports_filters_) {
75 arguments->ThrowTypeError("This event does not support filters");
76 return;
77 }
78
79 v8::Local<v8::Object> filter;
80 if (!arguments->PeekNext().IsEmpty() && !arguments->GetNext(&filter)) {
81 arguments->ThrowTypeError("Invalid invocation");
82 return;
83 }
84
85 v8::Local<v8::Context> context = arguments->GetHolderCreationContext();
86 if (!gin::PerContextData::From(context))
87 return;
88
89 std::string error;
90 if (!listeners_->AddListener(listener, filter, context, &error) &&
91 !error.empty()) {
92 arguments->ThrowTypeError(error);
93 }
94 }
95
96 void EventEmitter::RemoveListener(gin::Arguments* arguments) {
97 // See comment in AddListener().
98 if (!valid_)
99 return;
100
101 v8::Local<v8::Function> listener;
102 // See comment in AddListener().
103 if (!arguments->GetNext(&listener))
104 return;
105
106 listeners_->RemoveListener(listener, arguments->GetHolderCreationContext());
107 }
108
109 bool EventEmitter::HasListener(v8::Local<v8::Function> listener) {
110 return listeners_->HasListener(listener);
111 }
112
113 bool EventEmitter::HasListeners() {
114 return listeners_->GetNumListeners() != 0;
115 }
116
117 void EventEmitter::Dispatch(gin::Arguments* arguments) {
118 if (!valid_)
119 return;
120
121 if (listeners_->GetNumListeners() == 0)
122 return;
123
124 v8::Isolate* isolate = arguments->isolate();
125 v8::HandleScope handle_scope(isolate);
126 v8::Local<v8::Context> context = isolate->GetCurrentContext();
127 std::vector<v8::Local<v8::Value>> v8_args = arguments->GetAll();
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 // TODO(devlin): With more than 2^32 - 2 listeners, this can get nasty.
155 // We shouldn't reach that point, but it would be good to add enforcement.
156 CHECK(v8_responses
157 ->CreateDataProperty(context, i,
158 listener_responses[i].Get(isolate))
159 .ToChecked());
160 }
161
162 result = gin::DataObjectBuilder(isolate)
163 .Set("results", v8_responses.As<v8::Value>())
164 .Build();
165 }
166 arguments->Return(result);
167 }
168
169 void EventEmitter::DispatchImpl(
170 v8::Local<v8::Context> context,
171 std::vector<v8::Local<v8::Value>>* args,
172 const EventFilteringInfo* filter,
173 bool run_sync,
174 std::vector<v8::Global<v8::Value>>* out_values) {
175 // Note that |listeners_| can be modified during handling.
176 std::vector<v8::Local<v8::Function>> listeners =
177 listeners_->GetListeners(filter, context);
178
179 v8::Isolate* isolate = context->GetIsolate();
180 v8::TryCatch try_catch(isolate);
181 // SetVerbose() means the error will still get logged, which is what we
182 // want. We don't let it bubble up any further to prevent it from being
183 // surfaced in e.g. JS code that triggered the event.
184 try_catch.SetVerbose(true);
185 for (const auto& listener : listeners) {
186 if (run_sync) {
187 DCHECK(out_values);
188 v8::Global<v8::Value> result =
189 run_js_sync_.Run(listener, context, args->size(), args->data());
190 if (!result.IsEmpty() && !result.Get(isolate)->IsUndefined())
191 out_values->push_back(std::move(result));
192 } else {
193 run_js_.Run(listener, context, args->size(), args->data());
194 }
195 }
196 }
197
198 } // 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