blob: d48d8b502ed7999a58f3088b4954fd16974130bf [file] [log] [blame]
Devlin Cronin0b875672017-10-06 00:49:211// Copyright 2017 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/one_time_message_handler.h"
6
7#include <map>
8
9#include "base/bind.h"
10#include "base/callback.h"
11#include "base/stl_util.h"
12#include "base/supports_user_data.h"
13#include "content/public/renderer/render_frame.h"
14#include "extensions/common/api/messaging/message.h"
15#include "extensions/common/api/messaging/port_id.h"
Devlin Croninb15f7f02018-01-31 19:37:3216#include "extensions/renderer/bindings/api_binding_util.h"
Devlin Cronin0b875672017-10-06 00:49:2117#include "extensions/renderer/bindings/api_bindings_system.h"
18#include "extensions/renderer/bindings/api_event_handler.h"
19#include "extensions/renderer/bindings/api_request_handler.h"
Jens Widellece0eae2018-01-11 20:37:0520#include "extensions/renderer/bindings/get_per_context_data.h"
Devlin Cronin75bd36022017-11-22 19:03:0721#include "extensions/renderer/gc_callback.h"
Devlin Cronin0b875672017-10-06 00:49:2122#include "extensions/renderer/ipc_message_sender.h"
Devlin Croninb15f7f02018-01-31 19:37:3223#include "extensions/renderer/message_target.h"
Devlin Cronin0b875672017-10-06 00:49:2124#include "extensions/renderer/messaging_util.h"
25#include "extensions/renderer/native_extension_bindings_system.h"
26#include "extensions/renderer/script_context.h"
27#include "gin/arguments.h"
Devlin Cronine086c762017-12-21 15:48:4328#include "gin/dictionary.h"
Devlin Cronin0b875672017-10-06 00:49:2129#include "gin/handle.h"
30#include "gin/per_context_data.h"
31#include "ipc/ipc_message.h"
32
33namespace extensions {
34
35namespace {
36
Devlin Cronin0b875672017-10-06 00:49:2137// An opener port in the context; i.e., the caller of runtime.sendMessage.
38struct OneTimeOpener {
39 int request_id = -1;
40 int routing_id = MSG_ROUTING_NONE;
41};
42
43// A receiver port in the context; i.e., a listener to runtime.onMessage.
44struct OneTimeReceiver {
45 int routing_id = MSG_ROUTING_NONE;
Devlin Cronin182b0892017-11-10 22:22:1646 std::string event_name;
Devlin Cronin0b875672017-10-06 00:49:2147 v8::Global<v8::Object> sender;
48};
49
50using OneTimeMessageCallback =
51 base::OnceCallback<void(gin::Arguments* arguments)>;
52struct OneTimeMessageContextData : public base::SupportsUserData::Data {
Jens Widellece0eae2018-01-11 20:37:0553 static constexpr char kPerContextDataKey[] =
54 "extension_one_time_message_context_data";
55
Devlin Cronin0b875672017-10-06 00:49:2156 std::map<PortId, OneTimeOpener> openers;
57 std::map<PortId, OneTimeReceiver> receivers;
58 std::vector<std::unique_ptr<OneTimeMessageCallback>> pending_callbacks;
59};
60
Jens Widellece0eae2018-01-11 20:37:0561constexpr char OneTimeMessageContextData::kPerContextDataKey[];
Devlin Cronin0b875672017-10-06 00:49:2162
63int RoutingIdForScriptContext(ScriptContext* script_context) {
64 content::RenderFrame* render_frame = script_context->GetRenderFrame();
65 return render_frame ? render_frame->GetRoutingID() : MSG_ROUTING_NONE;
66}
67
68void OneTimeMessageResponseHelper(
69 const v8::FunctionCallbackInfo<v8::Value>& info) {
70 CHECK(info.Data()->IsExternal());
71
72 gin::Arguments arguments(info);
73 v8::Isolate* isolate = arguments.isolate();
74 v8::HandleScope handle_scope(isolate);
75 v8::Local<v8::Context> context = isolate->GetCurrentContext();
76
Jens Widellece0eae2018-01-11 20:37:0577 OneTimeMessageContextData* data =
78 GetPerContextData<OneTimeMessageContextData>(context,
79 kDontCreateIfMissing);
Devlin Cronin0b875672017-10-06 00:49:2180 if (!data)
81 return;
82
83 v8::Local<v8::External> external = info.Data().As<v8::External>();
84 auto* raw_callback = static_cast<OneTimeMessageCallback*>(external->Value());
85 auto iter = std::find_if(
86 data->pending_callbacks.begin(), data->pending_callbacks.end(),
87 [raw_callback](const std::unique_ptr<OneTimeMessageCallback>& callback) {
88 return callback.get() == raw_callback;
89 });
90 if (iter == data->pending_callbacks.end())
91 return;
92
93 std::unique_ptr<OneTimeMessageCallback> callback = std::move(*iter);
94 data->pending_callbacks.erase(iter);
95 std::move(*callback).Run(&arguments);
96}
97
Devlin Cronine086c762017-12-21 15:48:4398// Called with the results of dispatching an onMessage event to listeners.
99// Returns true if any of the listeners responded with `true`, indicating they
100// will respond to the call asynchronously.
101bool WillListenerReplyAsync(v8::Local<v8::Context> context,
Devlin Croninb15f7f02018-01-31 19:37:32102 v8::MaybeLocal<v8::Value> maybe_results) {
103 v8::Local<v8::Value> results;
104 // |maybe_results| can be empty if the context was destroyed before the
105 // listeners were ran (or while they were running).
106 if (!maybe_results.ToLocal(&results))
107 return false;
108
Devlin Cronine086c762017-12-21 15:48:43109 if (!results->IsObject())
110 return false;
111
112 // Suppress any script errors, but bail out if they happen (in theory, we
113 // shouldn't have any).
114 v8::Isolate* isolate = context->GetIsolate();
115 v8::TryCatch try_catch(isolate);
116 // We expect results in the form of an object with an array of results as
117 // a `results` property.
118 v8::Local<v8::Value> results_property;
119 if (!results.As<v8::Object>()
120 ->Get(context, gin::StringToSymbol(isolate, "results"))
121 .ToLocal(&results_property) ||
122 !results_property->IsArray()) {
123 return false;
124 }
125
126 // Check if any of the results is `true`.
127 v8::Local<v8::Array> array = results_property.As<v8::Array>();
128 uint32_t length = array->Length();
129 for (uint32_t i = 0; i < length; ++i) {
130 v8::Local<v8::Value> val;
131 if (!array->Get(context, i).ToLocal(&val))
132 return false;
133
134 if (val->IsTrue())
135 return true;
136 }
137
138 return false;
139}
140
Devlin Cronin0b875672017-10-06 00:49:21141} // namespace
142
143OneTimeMessageHandler::OneTimeMessageHandler(
144 NativeExtensionBindingsSystem* bindings_system)
Jeremy Roman9fc2de62019-07-12 14:15:03145 : bindings_system_(bindings_system) {}
Devlin Cronin0b875672017-10-06 00:49:21146OneTimeMessageHandler::~OneTimeMessageHandler() {}
147
148bool OneTimeMessageHandler::HasPort(ScriptContext* script_context,
149 const PortId& port_id) {
150 v8::Isolate* isolate = script_context->isolate();
151 v8::HandleScope handle_scope(isolate);
152
153 OneTimeMessageContextData* data =
Jens Widellece0eae2018-01-11 20:37:05154 GetPerContextData<OneTimeMessageContextData>(script_context->v8_context(),
155 kDontCreateIfMissing);
Devlin Cronin0b875672017-10-06 00:49:21156 if (!data)
157 return false;
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46158 return port_id.is_opener ? base::Contains(data->openers, port_id)
159 : base::Contains(data->receivers, port_id);
Devlin Cronin0b875672017-10-06 00:49:21160}
161
162void OneTimeMessageHandler::SendMessage(
163 ScriptContext* script_context,
164 const PortId& new_port_id,
Devlin Cronind4532eb2017-10-06 20:26:16165 const MessageTarget& target,
Devlin Cronin0b875672017-10-06 00:49:21166 const std::string& method_name,
Devlin Cronin0b875672017-10-06 00:49:21167 const Message& message,
168 v8::Local<v8::Function> response_callback) {
169 v8::Isolate* isolate = script_context->isolate();
170 v8::HandleScope handle_scope(isolate);
171
172 DCHECK(new_port_id.is_opener);
173 DCHECK_EQ(script_context->context_id(), new_port_id.context_id);
174
175 OneTimeMessageContextData* data =
Jens Widellece0eae2018-01-11 20:37:05176 GetPerContextData<OneTimeMessageContextData>(script_context->v8_context(),
177 kCreateIfMissing);
Devlin Cronin0b875672017-10-06 00:49:21178 DCHECK(data);
179
180 bool wants_response = !response_callback.IsEmpty();
181 int routing_id = RoutingIdForScriptContext(script_context);
182 if (wants_response) {
183 int request_id =
184 bindings_system_->api_system()->request_handler()->AddPendingRequest(
185 script_context->v8_context(), response_callback);
186 OneTimeOpener& port = data->openers[new_port_id];
187 port.request_id = request_id;
188 port.routing_id = routing_id;
189 }
190
191 IPCMessageSender* ipc_sender = bindings_system_->GetIPCMessageSender();
Devlin Cronind4532eb2017-10-06 20:26:16192 ipc_sender->SendOpenMessageChannel(script_context, new_port_id, target,
Nick Harper41374d52020-01-30 22:36:47193 method_name);
Istiaque Ahmed39619d82019-02-06 17:36:04194 ipc_sender->SendPostMessageToPort(new_port_id, message);
Devlin Cronin0b875672017-10-06 00:49:21195
Devlin Croninb15f7f02018-01-31 19:37:32196 // If the sender doesn't provide a response callback, we can immediately
197 // close the channel. Note: we only do this for extension messages, not
198 // native apps.
199 // TODO(devlin): This is because of some subtle ordering in the browser side,
200 // where closing the channel after sending the message causes things to be
201 // destroyed in the wrong order. That would be nice to fix.
202 if (!wants_response && target.type != MessageTarget::NATIVE_APP) {
Devlin Cronin0b875672017-10-06 00:49:21203 bool close_channel = true;
204 ipc_sender->SendCloseMessagePort(routing_id, new_port_id, close_channel);
205 }
206}
207
208void OneTimeMessageHandler::AddReceiver(ScriptContext* script_context,
209 const PortId& target_port_id,
210 v8::Local<v8::Object> sender,
Devlin Cronin182b0892017-11-10 22:22:16211 const std::string& event_name) {
Devlin Cronin0b875672017-10-06 00:49:21212 DCHECK(!target_port_id.is_opener);
213 DCHECK_NE(script_context->context_id(), target_port_id.context_id);
214
215 v8::Isolate* isolate = script_context->isolate();
216 v8::HandleScope handle_scope(isolate);
217 v8::Local<v8::Context> context = script_context->v8_context();
218
Jens Widellece0eae2018-01-11 20:37:05219 OneTimeMessageContextData* data =
220 GetPerContextData<OneTimeMessageContextData>(context, kCreateIfMissing);
Devlin Cronin0b875672017-10-06 00:49:21221 DCHECK(data);
Jan Wilken Dörrie0fd53a22019-06-07 09:55:46222 DCHECK(!base::Contains(data->receivers, target_port_id));
Devlin Cronin0b875672017-10-06 00:49:21223 OneTimeReceiver& receiver = data->receivers[target_port_id];
224 receiver.sender.Reset(isolate, sender);
225 receiver.routing_id = RoutingIdForScriptContext(script_context);
Devlin Cronin182b0892017-11-10 22:22:16226 receiver.event_name = event_name;
Devlin Cronin0b875672017-10-06 00:49:21227}
228
229bool OneTimeMessageHandler::DeliverMessage(ScriptContext* script_context,
230 const Message& message,
231 const PortId& target_port_id) {
232 v8::Isolate* isolate = script_context->isolate();
233 v8::HandleScope handle_scope(isolate);
234
235 return target_port_id.is_opener
236 ? DeliverReplyToOpener(script_context, message, target_port_id)
237 : DeliverMessageToReceiver(script_context, message,
238 target_port_id);
239}
240
241bool OneTimeMessageHandler::Disconnect(ScriptContext* script_context,
242 const PortId& port_id,
243 const std::string& error_message) {
244 v8::Isolate* isolate = script_context->isolate();
245 v8::HandleScope handle_scope(isolate);
246
247 return port_id.is_opener
248 ? DisconnectOpener(script_context, port_id, error_message)
249 : DisconnectReceiver(script_context, port_id);
250}
251
252bool OneTimeMessageHandler::DeliverMessageToReceiver(
253 ScriptContext* script_context,
254 const Message& message,
255 const PortId& target_port_id) {
256 DCHECK(!target_port_id.is_opener);
257
258 v8::Isolate* isolate = script_context->isolate();
259 v8::Local<v8::Context> context = script_context->v8_context();
260
261 bool handled = false;
262
Jens Widellece0eae2018-01-11 20:37:05263 OneTimeMessageContextData* data =
264 GetPerContextData<OneTimeMessageContextData>(context,
265 kDontCreateIfMissing);
Devlin Cronin0b875672017-10-06 00:49:21266 if (!data)
267 return handled;
268
269 auto iter = data->receivers.find(target_port_id);
270 if (iter == data->receivers.end())
271 return handled;
272
273 handled = true;
274 OneTimeReceiver& port = iter->second;
275
276 // This port is a receiver, so we invoke the onMessage event and provide a
277 // callback through which the port can respond. The port stays open until
278 // we receive a response.
279 // TODO(devlin): With chrome.runtime.sendMessage, we actually require that a
280 // listener return `true` if they intend to respond asynchronously; otherwise
Devlin Cronin75bd36022017-11-22 19:03:07281 // we close the port.
Devlin Cronin0b875672017-10-06 00:49:21282 auto callback = std::make_unique<OneTimeMessageCallback>(
283 base::Bind(&OneTimeMessageHandler::OnOneTimeMessageResponse,
284 weak_factory_.GetWeakPtr(), target_port_id));
285 v8::Local<v8::External> external = v8::External::New(isolate, callback.get());
286 v8::Local<v8::Function> response_function;
287
288 if (!v8::Function::New(context, &OneTimeMessageResponseHelper, external)
289 .ToLocal(&response_function)) {
290 NOTREACHED();
291 return handled;
292 }
293
Devlin Cronin75bd36022017-11-22 19:03:07294 // We shouldn't need to monitor context invalidation here. We store the ports
295 // for the context in PerContextData (cleaned up on context destruction), and
296 // the browser watches for frame navigation or destruction, and cleans up
297 // orphaned channels.
298 base::Closure on_context_invalidated;
299
300 new GCCallback(
301 script_context, response_function,
302 base::Bind(&OneTimeMessageHandler::OnResponseCallbackCollected,
303 weak_factory_.GetWeakPtr(), script_context, target_port_id),
304 base::Closure());
305
Devlin Cronin0b875672017-10-06 00:49:21306 v8::HandleScope handle_scope(isolate);
307 v8::Local<v8::Value> v8_message =
308 messaging_util::MessageToV8(context, message);
309 v8::Local<v8::Object> v8_sender = port.sender.Get(isolate);
310 std::vector<v8::Local<v8::Value>> args = {v8_message, v8_sender,
311 response_function};
312
Devlin Cronine086c762017-12-21 15:48:43313 JSRunner::ResultCallback dispatch_callback;
314 // For runtime.onMessage, we require that the listener return `true` if they
315 // intend to respond asynchronously. Check the results of the listeners.
316 if (port.event_name == messaging_util::kOnMessageEvent) {
317 dispatch_callback =
318 base::BindOnce(&OneTimeMessageHandler::OnEventFired,
319 weak_factory_.GetWeakPtr(), target_port_id);
320 }
321
Devlin Cronin0b875672017-10-06 00:49:21322 data->pending_callbacks.push_back(std::move(callback));
323 bindings_system_->api_system()->event_handler()->FireEventInContext(
Devlin Cronine086c762017-12-21 15:48:43324 port.event_name, context, &args, nullptr, std::move(dispatch_callback));
Devlin Cronin0b875672017-10-06 00:49:21325
Devlin Croninb15f7f02018-01-31 19:37:32326 // Note: The context could be invalidated at this point!
327
Devlin Cronin0b875672017-10-06 00:49:21328 return handled;
329}
330
331bool OneTimeMessageHandler::DeliverReplyToOpener(ScriptContext* script_context,
332 const Message& message,
333 const PortId& target_port_id) {
334 DCHECK(target_port_id.is_opener);
335
Devlin Croninb15f7f02018-01-31 19:37:32336 v8::Local<v8::Context> v8_context = script_context->v8_context();
Devlin Cronin0b875672017-10-06 00:49:21337 bool handled = false;
338
Jens Widellece0eae2018-01-11 20:37:05339 OneTimeMessageContextData* data =
Devlin Croninb15f7f02018-01-31 19:37:32340 GetPerContextData<OneTimeMessageContextData>(v8_context,
Jens Widellece0eae2018-01-11 20:37:05341 kDontCreateIfMissing);
Devlin Cronin0b875672017-10-06 00:49:21342 if (!data)
343 return handled;
344
345 auto iter = data->openers.find(target_port_id);
346 if (iter == data->openers.end())
347 return handled;
348
349 handled = true;
350
Devlin Croninb15f7f02018-01-31 19:37:32351 // Note: make a copy of port, since we're about to free it.
352 const OneTimeOpener port = iter->second;
Devlin Cronin0b875672017-10-06 00:49:21353 DCHECK_NE(-1, port.request_id);
354
Devlin Croninb15f7f02018-01-31 19:37:32355 // We erase the opener now, since delivering the reply can cause JS to run,
356 // which could either invalidate the context or modify the |openers|
357 // collection (e.g., by sending another message).
358 data->openers.erase(iter);
359
Devlin Cronin0b875672017-10-06 00:49:21360 // This port was the opener, so the message is the response from the
361 // receiver. Invoke the callback and close the message port.
362 v8::Local<v8::Value> v8_message =
Devlin Croninb15f7f02018-01-31 19:37:32363 messaging_util::MessageToV8(v8_context, message);
Devlin Cronin0b875672017-10-06 00:49:21364 std::vector<v8::Local<v8::Value>> args = {v8_message};
365 bindings_system_->api_system()->request_handler()->CompleteRequest(
366 port.request_id, args, std::string());
367
368 bool close_channel = true;
369 bindings_system_->GetIPCMessageSender()->SendCloseMessagePort(
370 port.routing_id, target_port_id, close_channel);
Devlin Croninb15f7f02018-01-31 19:37:32371
372 // Note: The context could be invalidated at this point!
Devlin Cronin0b875672017-10-06 00:49:21373
374 return handled;
375}
376
377bool OneTimeMessageHandler::DisconnectReceiver(ScriptContext* script_context,
378 const PortId& port_id) {
379 v8::Local<v8::Context> context = script_context->v8_context();
380 bool handled = false;
381
Jens Widellece0eae2018-01-11 20:37:05382 OneTimeMessageContextData* data =
383 GetPerContextData<OneTimeMessageContextData>(context,
384 kDontCreateIfMissing);
Devlin Cronin0b875672017-10-06 00:49:21385 if (!data)
386 return handled;
387
388 auto iter = data->receivers.find(port_id);
389 if (iter == data->receivers.end())
390 return handled;
391
392 handled = true;
393 data->receivers.erase(iter);
394 return handled;
395}
396
397bool OneTimeMessageHandler::DisconnectOpener(ScriptContext* script_context,
398 const PortId& port_id,
399 const std::string& error_message) {
400 bool handled = false;
401
Devlin Croninb15f7f02018-01-31 19:37:32402 v8::Local<v8::Context> v8_context = script_context->v8_context();
Devlin Cronin0b875672017-10-06 00:49:21403 OneTimeMessageContextData* data =
Devlin Croninb15f7f02018-01-31 19:37:32404 GetPerContextData<OneTimeMessageContextData>(v8_context,
Jens Widellece0eae2018-01-11 20:37:05405 kDontCreateIfMissing);
Devlin Cronin0b875672017-10-06 00:49:21406 if (!data)
407 return handled;
408
409 auto iter = data->openers.find(port_id);
410 if (iter == data->openers.end())
411 return handled;
412
413 handled = true;
Devlin Croninb15f7f02018-01-31 19:37:32414
415 // Note: make a copy of port, since we're about to free it.
416 const OneTimeOpener port = iter->second;
Devlin Cronin0b875672017-10-06 00:49:21417 DCHECK_NE(-1, port.request_id);
418
Devlin Croninb15f7f02018-01-31 19:37:32419 // We erase the opener now, since delivering the reply can cause JS to run,
420 // which could either invalidate the context or modify the |openers|
421 // collection (e.g., by sending another message).
Devlin Cronin0b875672017-10-06 00:49:21422 data->openers.erase(iter);
Devlin Croninb15f7f02018-01-31 19:37:32423
424 bindings_system_->api_system()->request_handler()->CompleteRequest(
425 port.request_id, std::vector<v8::Local<v8::Value>>(),
426 // If the browser doesn't supply an error message, we supply a generic
427 // one.
428 error_message.empty()
429 ? "The message port closed before a response was received."
430 : error_message);
431
432 // Note: The context could be invalidated at this point!
433
Devlin Cronin0b875672017-10-06 00:49:21434 return handled;
435}
436
437void OneTimeMessageHandler::OnOneTimeMessageResponse(
438 const PortId& port_id,
439 gin::Arguments* arguments) {
440 v8::Isolate* isolate = arguments->isolate();
441 v8::Local<v8::Context> context = isolate->GetCurrentContext();
Devlin Cronine086c762017-12-21 15:48:43442
443 // The listener may try replying after the context or the channel has been
444 // closed. Fail gracefully.
445 // TODO(devlin): At least in the case of the channel being closed (e.g.
446 // because the listener did not return `true`), it might be good to surface an
447 // error.
Jens Widellece0eae2018-01-11 20:37:05448 OneTimeMessageContextData* data =
449 GetPerContextData<OneTimeMessageContextData>(context,
450 kDontCreateIfMissing);
Devlin Cronine086c762017-12-21 15:48:43451 if (!data)
452 return;
453
Devlin Cronin0b875672017-10-06 00:49:21454 auto iter = data->receivers.find(port_id);
Devlin Cronine086c762017-12-21 15:48:43455 if (iter == data->receivers.end())
456 return;
457
Devlin Cronin0b875672017-10-06 00:49:21458 int routing_id = iter->second.routing_id;
459 data->receivers.erase(iter);
460
Devlin Cronin0b875672017-10-06 00:49:21461 v8::Local<v8::Value> value;
Devlin Croninb15f7f02018-01-31 19:37:32462 // We allow omitting the message argument (e.g., sendMessage()). Default the
463 // value to undefined.
464 if (arguments->Length() > 0)
465 CHECK(arguments->GetNext(&value));
466 else
467 value = v8::Undefined(isolate);
Devlin Cronin0b875672017-10-06 00:49:21468
Devlin Croninfe7aae62017-11-16 03:49:55469 std::string error;
Devlin Cronin0b875672017-10-06 00:49:21470 std::unique_ptr<Message> message =
Devlin Croninfe7aae62017-11-16 03:49:55471 messaging_util::MessageFromV8(context, value, &error);
Devlin Cronin0b875672017-10-06 00:49:21472 if (!message) {
Devlin Croninfe7aae62017-11-16 03:49:55473 arguments->ThrowTypeError(error);
Devlin Cronin0b875672017-10-06 00:49:21474 return;
475 }
476 IPCMessageSender* ipc_sender = bindings_system_->GetIPCMessageSender();
Istiaque Ahmed39619d82019-02-06 17:36:04477 ipc_sender->SendPostMessageToPort(port_id, *message);
Devlin Cronin0b875672017-10-06 00:49:21478 bool close_channel = true;
479 ipc_sender->SendCloseMessagePort(routing_id, port_id, close_channel);
480}
481
Devlin Cronin75bd36022017-11-22 19:03:07482void OneTimeMessageHandler::OnResponseCallbackCollected(
483 ScriptContext* script_context,
484 const PortId& port_id) {
485 // Note: we know |script_context| is still valid because the GC callback won't
486 // be called after context invalidation.
487 v8::HandleScope handle_scope(script_context->isolate());
488 OneTimeMessageContextData* data =
Jens Widellece0eae2018-01-11 20:37:05489 GetPerContextData<OneTimeMessageContextData>(script_context->v8_context(),
490 kDontCreateIfMissing);
Devlin Cronin75bd36022017-11-22 19:03:07491 // ScriptContext invalidation and PerContextData cleanup happen "around" the
492 // same time, but there aren't strict guarantees about ordering. It's possible
493 // the data was collected.
494 if (!data)
495 return;
496
497 auto iter = data->receivers.find(port_id);
498 // The channel may already be closed (if the receiver replied before the reply
499 // callback was collected).
500 if (iter == data->receivers.end())
501 return;
502
503 int routing_id = iter->second.routing_id;
504 data->receivers.erase(iter);
505
506 // Close the message port. There's no way to send a reply anymore. Don't
507 // close the channel because another listener may reply.
508 IPCMessageSender* ipc_sender = bindings_system_->GetIPCMessageSender();
509 bool close_channel = false;
510 ipc_sender->SendCloseMessagePort(routing_id, port_id, close_channel);
511}
512
Devlin Cronine086c762017-12-21 15:48:43513void OneTimeMessageHandler::OnEventFired(const PortId& port_id,
514 v8::Local<v8::Context> context,
515 v8::MaybeLocal<v8::Value> result) {
516 // The context could be tearing down by the time the event is fully
517 // dispatched.
Jens Widellece0eae2018-01-11 20:37:05518 OneTimeMessageContextData* data =
519 GetPerContextData<OneTimeMessageContextData>(context,
520 kDontCreateIfMissing);
Devlin Cronine086c762017-12-21 15:48:43521 if (!data)
522 return;
523
Devlin Croninb15f7f02018-01-31 19:37:32524 if (WillListenerReplyAsync(context, result))
Devlin Cronine086c762017-12-21 15:48:43525 return; // The listener will reply later; leave the channel open.
526
527 auto iter = data->receivers.find(port_id);
528 // The channel may already be closed (if the listener replied).
529 if (iter == data->receivers.end())
530 return;
531
532 int routing_id = iter->second.routing_id;
533 data->receivers.erase(iter);
534
535 // The listener did not reply and did not return `true` from any of its
536 // listeners. Close the message port. Don't close the channel because another
537 // listener (in a separate context) may reply.
538 IPCMessageSender* ipc_sender = bindings_system_->GetIPCMessageSender();
539 bool close_channel = false;
540 ipc_sender->SendCloseMessagePort(routing_id, port_id, close_channel);
541}
542
Devlin Cronin0b875672017-10-06 00:49:21543} // namespace extensions