one_time_message_handler.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. // 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. #include "extensions/renderer/one_time_message_handler.h"
  5. #include <map>
  6. #include "base/bind.h"
  7. #include "base/callback.h"
  8. #include "base/containers/contains.h"
  9. #include "base/containers/cxx20_erase.h"
  10. #include "base/supports_user_data.h"
  11. #include "content/public/renderer/render_frame.h"
  12. #include "extensions/common/api/messaging/message.h"
  13. #include "extensions/common/api/messaging/port_id.h"
  14. #include "extensions/common/mojom/event_dispatcher.mojom.h"
  15. #include "extensions/renderer/bindings/api_binding_types.h"
  16. #include "extensions/renderer/bindings/api_binding_util.h"
  17. #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"
  20. #include "extensions/renderer/bindings/get_per_context_data.h"
  21. #include "extensions/renderer/gc_callback.h"
  22. #include "extensions/renderer/ipc_message_sender.h"
  23. #include "extensions/renderer/message_target.h"
  24. #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"
  28. #include "gin/dictionary.h"
  29. #include "gin/handle.h"
  30. #include "gin/per_context_data.h"
  31. #include "ipc/ipc_message.h"
  32. #include "v8/include/v8-container.h"
  33. #include "v8/include/v8-exception.h"
  34. #include "v8/include/v8-external.h"
  35. #include "v8/include/v8-function-callback.h"
  36. #include "v8/include/v8-function.h"
  37. #include "v8/include/v8-isolate.h"
  38. #include "v8/include/v8-object.h"
  39. #include "v8/include/v8-persistent-handle.h"
  40. #include "v8/include/v8-primitive.h"
  41. namespace extensions {
  42. namespace {
  43. // An opener port in the context; i.e., the caller of runtime.sendMessage.
  44. struct OneTimeOpener {
  45. int request_id = -1;
  46. int routing_id = MSG_ROUTING_NONE;
  47. binding::AsyncResponseType async_type = binding::AsyncResponseType::kNone;
  48. };
  49. // A receiver port in the context; i.e., a listener to runtime.onMessage.
  50. struct OneTimeReceiver {
  51. int routing_id = MSG_ROUTING_NONE;
  52. std::string event_name;
  53. v8::Global<v8::Object> sender;
  54. };
  55. using OneTimeMessageCallback =
  56. base::OnceCallback<void(gin::Arguments* arguments)>;
  57. struct OneTimeMessageContextData : public base::SupportsUserData::Data {
  58. static constexpr char kPerContextDataKey[] =
  59. "extension_one_time_message_context_data";
  60. std::map<PortId, OneTimeOpener> openers;
  61. std::map<PortId, OneTimeReceiver> receivers;
  62. std::vector<std::unique_ptr<OneTimeMessageCallback>> pending_callbacks;
  63. };
  64. constexpr char OneTimeMessageContextData::kPerContextDataKey[];
  65. int RoutingIdForScriptContext(ScriptContext* script_context) {
  66. content::RenderFrame* render_frame = script_context->GetRenderFrame();
  67. return render_frame ? render_frame->GetRoutingID() : MSG_ROUTING_NONE;
  68. }
  69. void OneTimeMessageResponseHelper(
  70. const v8::FunctionCallbackInfo<v8::Value>& info) {
  71. CHECK(info.Data()->IsExternal());
  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. OneTimeMessageContextData* data =
  77. GetPerContextData<OneTimeMessageContextData>(context,
  78. kDontCreateIfMissing);
  79. if (!data)
  80. return;
  81. v8::Local<v8::External> external = info.Data().As<v8::External>();
  82. auto* raw_callback = static_cast<OneTimeMessageCallback*>(external->Value());
  83. auto iter = std::find_if(
  84. data->pending_callbacks.begin(), data->pending_callbacks.end(),
  85. [raw_callback](const std::unique_ptr<OneTimeMessageCallback>& callback) {
  86. return callback.get() == raw_callback;
  87. });
  88. if (iter == data->pending_callbacks.end())
  89. return;
  90. std::unique_ptr<OneTimeMessageCallback> callback = std::move(*iter);
  91. data->pending_callbacks.erase(iter);
  92. std::move(*callback).Run(&arguments);
  93. }
  94. // Called with the results of dispatching an onMessage event to listeners.
  95. // Returns true if any of the listeners responded with `true`, indicating they
  96. // will respond to the call asynchronously.
  97. bool WillListenerReplyAsync(v8::Local<v8::Context> context,
  98. v8::MaybeLocal<v8::Value> maybe_results) {
  99. v8::Local<v8::Value> results;
  100. // |maybe_results| can be empty if the context was destroyed before the
  101. // listeners were ran (or while they were running).
  102. if (!maybe_results.ToLocal(&results))
  103. return false;
  104. if (!results->IsObject())
  105. return false;
  106. // Suppress any script errors, but bail out if they happen (in theory, we
  107. // shouldn't have any).
  108. v8::Isolate* isolate = context->GetIsolate();
  109. v8::TryCatch try_catch(isolate);
  110. // We expect results in the form of an object with an array of results as
  111. // a `results` property.
  112. v8::Local<v8::Value> results_property;
  113. if (!results.As<v8::Object>()
  114. ->Get(context, gin::StringToSymbol(isolate, "results"))
  115. .ToLocal(&results_property) ||
  116. !results_property->IsArray()) {
  117. return false;
  118. }
  119. // Check if any of the results is `true`.
  120. v8::Local<v8::Array> array = results_property.As<v8::Array>();
  121. uint32_t length = array->Length();
  122. for (uint32_t i = 0; i < length; ++i) {
  123. v8::Local<v8::Value> val;
  124. if (!array->Get(context, i).ToLocal(&val))
  125. return false;
  126. if (val->IsTrue())
  127. return true;
  128. }
  129. return false;
  130. }
  131. } // namespace
  132. OneTimeMessageHandler::OneTimeMessageHandler(
  133. NativeExtensionBindingsSystem* bindings_system)
  134. : bindings_system_(bindings_system) {}
  135. OneTimeMessageHandler::~OneTimeMessageHandler() {}
  136. bool OneTimeMessageHandler::HasPort(ScriptContext* script_context,
  137. const PortId& port_id) {
  138. v8::Isolate* isolate = script_context->isolate();
  139. v8::HandleScope handle_scope(isolate);
  140. OneTimeMessageContextData* data =
  141. GetPerContextData<OneTimeMessageContextData>(script_context->v8_context(),
  142. kDontCreateIfMissing);
  143. if (!data)
  144. return false;
  145. return port_id.is_opener ? base::Contains(data->openers, port_id)
  146. : base::Contains(data->receivers, port_id);
  147. }
  148. v8::Local<v8::Promise> OneTimeMessageHandler::SendMessage(
  149. ScriptContext* script_context,
  150. const PortId& new_port_id,
  151. const MessageTarget& target,
  152. const std::string& method_name,
  153. const Message& message,
  154. binding::AsyncResponseType async_type,
  155. v8::Local<v8::Function> response_callback) {
  156. v8::Isolate* isolate = script_context->isolate();
  157. v8::EscapableHandleScope handle_scope(isolate);
  158. DCHECK(new_port_id.is_opener);
  159. DCHECK_EQ(script_context->context_id(), new_port_id.context_id);
  160. OneTimeMessageContextData* data =
  161. GetPerContextData<OneTimeMessageContextData>(script_context->v8_context(),
  162. kCreateIfMissing);
  163. DCHECK(data);
  164. v8::Local<v8::Promise> promise;
  165. bool wants_response = async_type != binding::AsyncResponseType::kNone;
  166. int routing_id = RoutingIdForScriptContext(script_context);
  167. if (wants_response) {
  168. // If this is a promise based request no callback should have been passed
  169. // in.
  170. if (async_type == binding::AsyncResponseType::kPromise)
  171. DCHECK(response_callback.IsEmpty());
  172. APIRequestHandler::RequestDetails details =
  173. bindings_system_->api_system()->request_handler()->AddPendingRequest(
  174. script_context->v8_context(), async_type, response_callback,
  175. binding::ResultModifierFunction());
  176. OneTimeOpener& port = data->openers[new_port_id];
  177. port.request_id = details.request_id;
  178. port.routing_id = routing_id;
  179. port.async_type = async_type;
  180. promise = details.promise;
  181. DCHECK_EQ(async_type == binding::AsyncResponseType::kPromise,
  182. !promise.IsEmpty());
  183. }
  184. IPCMessageSender* ipc_sender = bindings_system_->GetIPCMessageSender();
  185. ipc_sender->SendOpenMessageChannel(script_context, new_port_id, target,
  186. method_name);
  187. ipc_sender->SendPostMessageToPort(new_port_id, message);
  188. // If the sender doesn't provide a response callback, we can immediately
  189. // close the channel. Note: we only do this for extension messages, not
  190. // native apps.
  191. // TODO(devlin): This is because of some subtle ordering in the browser side,
  192. // where closing the channel after sending the message causes things to be
  193. // destroyed in the wrong order. That would be nice to fix.
  194. if (!wants_response && target.type != MessageTarget::NATIVE_APP) {
  195. bool close_channel = true;
  196. ipc_sender->SendCloseMessagePort(routing_id, new_port_id, close_channel);
  197. }
  198. return handle_scope.Escape(promise);
  199. }
  200. void OneTimeMessageHandler::AddReceiver(ScriptContext* script_context,
  201. const PortId& target_port_id,
  202. v8::Local<v8::Object> sender,
  203. const std::string& event_name) {
  204. DCHECK(!target_port_id.is_opener);
  205. DCHECK_NE(script_context->context_id(), target_port_id.context_id);
  206. v8::Isolate* isolate = script_context->isolate();
  207. v8::HandleScope handle_scope(isolate);
  208. v8::Local<v8::Context> context = script_context->v8_context();
  209. OneTimeMessageContextData* data =
  210. GetPerContextData<OneTimeMessageContextData>(context, kCreateIfMissing);
  211. DCHECK(data);
  212. DCHECK(!base::Contains(data->receivers, target_port_id));
  213. OneTimeReceiver& receiver = data->receivers[target_port_id];
  214. receiver.sender.Reset(isolate, sender);
  215. receiver.routing_id = RoutingIdForScriptContext(script_context);
  216. receiver.event_name = event_name;
  217. }
  218. bool OneTimeMessageHandler::DeliverMessage(ScriptContext* script_context,
  219. const Message& message,
  220. const PortId& target_port_id) {
  221. v8::Isolate* isolate = script_context->isolate();
  222. v8::HandleScope handle_scope(isolate);
  223. return target_port_id.is_opener
  224. ? DeliverReplyToOpener(script_context, message, target_port_id)
  225. : DeliverMessageToReceiver(script_context, message,
  226. target_port_id);
  227. }
  228. bool OneTimeMessageHandler::Disconnect(ScriptContext* script_context,
  229. const PortId& port_id,
  230. const std::string& error_message) {
  231. v8::Isolate* isolate = script_context->isolate();
  232. v8::HandleScope handle_scope(isolate);
  233. return port_id.is_opener
  234. ? DisconnectOpener(script_context, port_id, error_message)
  235. : DisconnectReceiver(script_context, port_id);
  236. }
  237. int OneTimeMessageHandler::GetPendingCallbackCountForTest(
  238. ScriptContext* script_context) {
  239. v8::Isolate* isolate = script_context->isolate();
  240. v8::HandleScope handle_scope(isolate);
  241. OneTimeMessageContextData* data =
  242. GetPerContextData<OneTimeMessageContextData>(script_context->v8_context(),
  243. kDontCreateIfMissing);
  244. return data ? data->pending_callbacks.size() : 0;
  245. }
  246. bool OneTimeMessageHandler::DeliverMessageToReceiver(
  247. ScriptContext* script_context,
  248. const Message& message,
  249. const PortId& target_port_id) {
  250. DCHECK(!target_port_id.is_opener);
  251. v8::Isolate* isolate = script_context->isolate();
  252. v8::Local<v8::Context> context = script_context->v8_context();
  253. bool handled = false;
  254. OneTimeMessageContextData* data =
  255. GetPerContextData<OneTimeMessageContextData>(context,
  256. kDontCreateIfMissing);
  257. if (!data)
  258. return handled;
  259. auto iter = data->receivers.find(target_port_id);
  260. if (iter == data->receivers.end())
  261. return handled;
  262. handled = true;
  263. OneTimeReceiver& port = iter->second;
  264. // This port is a receiver, so we invoke the onMessage event and provide a
  265. // callback through which the port can respond. The port stays open until we
  266. // receive a response.
  267. // TODO(devlin): With chrome.runtime.sendMessage, we actually require that a
  268. // listener return `true` if they intend to respond asynchronously; otherwise
  269. // we close the port.
  270. auto callback = std::make_unique<OneTimeMessageCallback>(
  271. base::BindOnce(&OneTimeMessageHandler::OnOneTimeMessageResponse,
  272. weak_factory_.GetWeakPtr(), target_port_id));
  273. v8::Local<v8::External> external = v8::External::New(isolate, callback.get());
  274. v8::Local<v8::Function> response_function;
  275. if (!v8::Function::New(context, &OneTimeMessageResponseHelper, external)
  276. .ToLocal(&response_function)) {
  277. NOTREACHED();
  278. return handled;
  279. }
  280. new GCCallback(
  281. script_context, response_function,
  282. base::BindOnce(&OneTimeMessageHandler::OnResponseCallbackCollected,
  283. weak_factory_.GetWeakPtr(), script_context, target_port_id,
  284. callback.get()),
  285. base::OnceClosure());
  286. v8::HandleScope handle_scope(isolate);
  287. v8::Local<v8::Value> v8_message =
  288. messaging_util::MessageToV8(context, message);
  289. v8::Local<v8::Object> v8_sender = port.sender.Get(isolate);
  290. std::vector<v8::Local<v8::Value>> args = {v8_message, v8_sender,
  291. response_function};
  292. JSRunner::ResultCallback dispatch_callback;
  293. // For runtime.onMessage, we require that the listener return `true` if they
  294. // intend to respond asynchronously. Check the results of the listeners.
  295. if (port.event_name == messaging_util::kOnMessageEvent) {
  296. dispatch_callback =
  297. base::BindOnce(&OneTimeMessageHandler::OnEventFired,
  298. weak_factory_.GetWeakPtr(), target_port_id);
  299. }
  300. data->pending_callbacks.push_back(std::move(callback));
  301. bindings_system_->api_system()->event_handler()->FireEventInContext(
  302. port.event_name, context, &args, nullptr, std::move(dispatch_callback));
  303. // Note: The context could be invalidated at this point!
  304. return handled;
  305. }
  306. bool OneTimeMessageHandler::DeliverReplyToOpener(ScriptContext* script_context,
  307. const Message& message,
  308. const PortId& target_port_id) {
  309. DCHECK(target_port_id.is_opener);
  310. v8::Local<v8::Context> v8_context = script_context->v8_context();
  311. bool handled = false;
  312. OneTimeMessageContextData* data =
  313. GetPerContextData<OneTimeMessageContextData>(v8_context,
  314. kDontCreateIfMissing);
  315. if (!data)
  316. return handled;
  317. auto iter = data->openers.find(target_port_id);
  318. if (iter == data->openers.end())
  319. return handled;
  320. handled = true;
  321. // Note: make a copy of port, since we're about to free it.
  322. const OneTimeOpener port = iter->second;
  323. DCHECK_NE(-1, port.request_id);
  324. // We erase the opener now, since delivering the reply can cause JS to run,
  325. // which could either invalidate the context or modify the |openers|
  326. // collection (e.g., by sending another message).
  327. data->openers.erase(iter);
  328. // This port was the opener, so the message is the response from the
  329. // receiver. Invoke the callback and close the message port.
  330. v8::Local<v8::Value> v8_message =
  331. messaging_util::MessageToV8(v8_context, message);
  332. std::vector<v8::Local<v8::Value>> args = {v8_message};
  333. bindings_system_->api_system()->request_handler()->CompleteRequest(
  334. port.request_id, args, std::string());
  335. bool close_channel = true;
  336. bindings_system_->GetIPCMessageSender()->SendCloseMessagePort(
  337. port.routing_id, target_port_id, close_channel);
  338. // Note: The context could be invalidated at this point!
  339. return handled;
  340. }
  341. bool OneTimeMessageHandler::DisconnectReceiver(ScriptContext* script_context,
  342. const PortId& port_id) {
  343. v8::Local<v8::Context> context = script_context->v8_context();
  344. bool handled = false;
  345. OneTimeMessageContextData* data =
  346. GetPerContextData<OneTimeMessageContextData>(context,
  347. kDontCreateIfMissing);
  348. if (!data)
  349. return handled;
  350. auto iter = data->receivers.find(port_id);
  351. if (iter == data->receivers.end())
  352. return handled;
  353. handled = true;
  354. data->receivers.erase(iter);
  355. return handled;
  356. }
  357. bool OneTimeMessageHandler::DisconnectOpener(ScriptContext* script_context,
  358. const PortId& port_id,
  359. const std::string& error_message) {
  360. bool handled = false;
  361. v8::Local<v8::Context> v8_context = script_context->v8_context();
  362. OneTimeMessageContextData* data =
  363. GetPerContextData<OneTimeMessageContextData>(v8_context,
  364. kDontCreateIfMissing);
  365. if (!data)
  366. return handled;
  367. auto iter = data->openers.find(port_id);
  368. if (iter == data->openers.end())
  369. return handled;
  370. handled = true;
  371. // Note: make a copy of port, since we're about to free it.
  372. const OneTimeOpener opener = iter->second;
  373. DCHECK_NE(-1, opener.request_id);
  374. // We erase the opener now, since delivering the reply can cause JS to run,
  375. // which could either invalidate the context or modify the |openers|
  376. // collection (e.g., by sending another message).
  377. data->openers.erase(iter);
  378. std::string error;
  379. // Set the error for the message port. If the browser supplies an error, we
  380. // always use that. Otherwise, the behavior is different for promise-based vs
  381. // callback-based channels.
  382. // For a promise-based channel, not receiving a response is fine (assuming the
  383. // listener didn't indicate it would send one) - the extension may simply be
  384. // waiting for confirmation that the message sent.
  385. // In the callback-based scenario, we use the presence of the callback as an
  386. // indication that the extension expected a specific response. This is an
  387. // unfortunate behavior difference that we keep for backwards-compatibility in
  388. // callback-based API calls.
  389. if (!error_message.empty()) {
  390. // If the browser supplied us with an error message, use that.
  391. error = error_message;
  392. } else if (opener.async_type == binding::AsyncResponseType::kCallback) {
  393. error = "The message port closed before a response was received.";
  394. }
  395. bindings_system_->api_system()->request_handler()->CompleteRequest(
  396. opener.request_id, std::vector<v8::Local<v8::Value>>(), error);
  397. // Note: The context could be invalidated at this point!
  398. return handled;
  399. }
  400. void OneTimeMessageHandler::OnOneTimeMessageResponse(
  401. const PortId& port_id,
  402. gin::Arguments* arguments) {
  403. v8::Isolate* isolate = arguments->isolate();
  404. v8::Local<v8::Context> context = isolate->GetCurrentContext();
  405. // The listener may try replying after the context or the channel has been
  406. // closed. Fail gracefully.
  407. // TODO(devlin): At least in the case of the channel being closed (e.g.
  408. // because the listener did not return `true`), it might be good to surface an
  409. // error.
  410. OneTimeMessageContextData* data =
  411. GetPerContextData<OneTimeMessageContextData>(context,
  412. kDontCreateIfMissing);
  413. if (!data)
  414. return;
  415. auto iter = data->receivers.find(port_id);
  416. if (iter == data->receivers.end())
  417. return;
  418. int routing_id = iter->second.routing_id;
  419. data->receivers.erase(iter);
  420. v8::Local<v8::Value> value;
  421. // We allow omitting the message argument (e.g., sendMessage()). Default the
  422. // value to undefined.
  423. if (arguments->Length() > 0)
  424. CHECK(arguments->GetNext(&value));
  425. else
  426. value = v8::Undefined(isolate);
  427. std::string error;
  428. std::unique_ptr<Message> message = messaging_util::MessageFromV8(
  429. context, value, port_id.serialization_format, &error);
  430. if (!message) {
  431. arguments->ThrowTypeError(error);
  432. return;
  433. }
  434. IPCMessageSender* ipc_sender = bindings_system_->GetIPCMessageSender();
  435. ipc_sender->SendPostMessageToPort(port_id, *message);
  436. bool close_channel = true;
  437. ipc_sender->SendCloseMessagePort(routing_id, port_id, close_channel);
  438. }
  439. void OneTimeMessageHandler::OnResponseCallbackCollected(
  440. ScriptContext* script_context,
  441. const PortId& port_id,
  442. void* raw_callback) {
  443. // Note: we know |script_context| is still valid because the GC callback won't
  444. // be called after context invalidation.
  445. v8::HandleScope handle_scope(script_context->isolate());
  446. OneTimeMessageContextData* data =
  447. GetPerContextData<OneTimeMessageContextData>(script_context->v8_context(),
  448. kDontCreateIfMissing);
  449. // ScriptContext invalidation and PerContextData cleanup happen "around" the
  450. // same time, but there aren't strict guarantees about ordering. It's possible
  451. // the data was collected.
  452. if (!data)
  453. return;
  454. auto iter = data->receivers.find(port_id);
  455. // The channel may already be closed (if the receiver replied before the reply
  456. // callback was collected).
  457. if (iter == data->receivers.end())
  458. return;
  459. int routing_id = iter->second.routing_id;
  460. data->receivers.erase(iter);
  461. // Since there is no way to call the callback anymore, we can remove it from
  462. // the pending callbacks.
  463. base::EraseIf(
  464. data->pending_callbacks,
  465. [raw_callback](const std::unique_ptr<OneTimeMessageCallback>& callback) {
  466. return callback.get() == raw_callback;
  467. });
  468. // Close the message port. There's no way to send a reply anymore. Don't
  469. // close the channel because another listener may reply.
  470. IPCMessageSender* ipc_sender = bindings_system_->GetIPCMessageSender();
  471. bool close_channel = false;
  472. ipc_sender->SendCloseMessagePort(routing_id, port_id, close_channel);
  473. }
  474. void OneTimeMessageHandler::OnEventFired(const PortId& port_id,
  475. v8::Local<v8::Context> context,
  476. v8::MaybeLocal<v8::Value> result) {
  477. // The context could be tearing down by the time the event is fully
  478. // dispatched.
  479. OneTimeMessageContextData* data =
  480. GetPerContextData<OneTimeMessageContextData>(context,
  481. kDontCreateIfMissing);
  482. if (!data)
  483. return;
  484. auto iter = data->receivers.find(port_id);
  485. // The channel may already be closed (if the listener replied).
  486. if (iter == data->receivers.end())
  487. return;
  488. int routing_id = iter->second.routing_id;
  489. IPCMessageSender* ipc_sender = bindings_system_->GetIPCMessageSender();
  490. if (WillListenerReplyAsync(context, result)) {
  491. // Inform the browser that one of the listeners said they would be replying
  492. // later and leave the channel open.
  493. ipc_sender->SendMessageResponsePending(routing_id, port_id);
  494. return;
  495. }
  496. data->receivers.erase(iter);
  497. // The listener did not reply and did not return `true` from any of its
  498. // listeners. Close the message port. Don't close the channel because another
  499. // listener (in a separate context) may reply.
  500. bool close_channel = false;
  501. ipc_sender->SendCloseMessagePort(routing_id, port_id, close_channel);
  502. }
  503. } // namespace extensions