native_renderer_messaging_service.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  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/native_renderer_messaging_service.h"
  5. #include <map>
  6. #include <memory>
  7. #include <string>
  8. #include <utility>
  9. #include <vector>
  10. #include "base/bind.h"
  11. #include "base/callback.h"
  12. #include "base/containers/contains.h"
  13. #include "base/supports_user_data.h"
  14. #include "content/public/common/child_process_host.h"
  15. #include "content/public/renderer/render_frame.h"
  16. #include "content/public/renderer/v8_value_converter.h"
  17. #include "extensions/common/api/messaging/message.h"
  18. #include "extensions/common/api/messaging/messaging_endpoint.h"
  19. #include "extensions/common/api/messaging/port_id.h"
  20. #include "extensions/common/extension_messages.h"
  21. #include "extensions/common/features/feature.h"
  22. #include "extensions/common/manifest_handlers/externally_connectable.h"
  23. #include "extensions/renderer/api_activity_logger.h"
  24. #include "extensions/renderer/bindings/api_binding_util.h"
  25. #include "extensions/renderer/bindings/get_per_context_data.h"
  26. #include "extensions/renderer/extension_interaction_provider.h"
  27. #include "extensions/renderer/get_script_context.h"
  28. #include "extensions/renderer/ipc_message_sender.h"
  29. #include "extensions/renderer/message_target.h"
  30. #include "extensions/renderer/messaging_util.h"
  31. #include "extensions/renderer/native_extension_bindings_system.h"
  32. #include "extensions/renderer/script_context.h"
  33. #include "extensions/renderer/script_context_set.h"
  34. #include "extensions/renderer/script_context_set_iterable.h"
  35. #include "gin/data_object_builder.h"
  36. #include "gin/handle.h"
  37. #include "gin/per_context_data.h"
  38. #include "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom.h"
  39. #include "third_party/blink/public/web/web_document.h"
  40. #include "third_party/blink/public/web/web_local_frame.h"
  41. #include "third_party/blink/public/web/web_scoped_window_focus_allowed_indicator.h"
  42. #include "v8/include/v8-context.h"
  43. #include "v8/include/v8-local-handle.h"
  44. #include "v8/include/v8-persistent-handle.h"
  45. using blink::mojom::UserActivationNotificationType;
  46. namespace extensions {
  47. namespace {
  48. struct MessagingPerContextData : public base::SupportsUserData::Data {
  49. static constexpr char kPerContextDataKey[] =
  50. "extension_messaging_per_context_data";
  51. // All the port objects that exist in this context.
  52. std::map<PortId, v8::Global<v8::Object>> ports;
  53. // The next available context-specific port id.
  54. int next_port_id = 0;
  55. };
  56. constexpr char MessagingPerContextData::kPerContextDataKey[];
  57. bool ScriptContextIsValid(ScriptContext* script_context) {
  58. // TODO(devlin): This is in lieu of a similar check in the JS bindings that
  59. // null-checks ScriptContext::GetRenderFrame(). This is good because it
  60. // removes the reliance on RenderFrames (which make testing very difficult),
  61. // but is it fully analogous? It should be close enough, since the browser
  62. // has to deal with frames potentially disappearing before the IPC arrives
  63. // anyway.
  64. return script_context->is_valid();
  65. }
  66. } // namespace
  67. NativeRendererMessagingService::NativeRendererMessagingService(
  68. NativeExtensionBindingsSystem* bindings_system)
  69. : bindings_system_(bindings_system),
  70. one_time_message_handler_(bindings_system) {}
  71. NativeRendererMessagingService::~NativeRendererMessagingService() {}
  72. void NativeRendererMessagingService::ValidateMessagePort(
  73. ScriptContextSetIterable* context_set,
  74. const PortId& port_id,
  75. content::RenderFrame* render_frame) {
  76. // TODO(devlin): In practice, |render_frame| should never be null here (unlike
  77. // in other methods, where it legitimately can), but it can be in testing. It
  78. // would be better to fake it somehow, but unfortunately, there's no good way
  79. // to have a RenderFrame in a unittest. :(
  80. int routing_id =
  81. render_frame ? render_frame->GetRoutingID() : MSG_ROUTING_NONE;
  82. bool has_port = false;
  83. // The base::Unretained() below is safe since ScriptContextSet::ForEach is
  84. // synchronous.
  85. context_set->ForEach(
  86. render_frame,
  87. base::BindRepeating(
  88. &NativeRendererMessagingService::ValidateMessagePortInContext,
  89. base::Unretained(this), port_id, &has_port));
  90. // A reply is only sent if the port is missing, because the port is assumed to
  91. // exist unless stated otherwise.
  92. if (!has_port) {
  93. bindings_system_->GetIPCMessageSender()->SendCloseMessagePort(
  94. routing_id, port_id, false);
  95. }
  96. }
  97. void NativeRendererMessagingService::DispatchOnConnect(
  98. ScriptContextSetIterable* context_set,
  99. const PortId& target_port_id,
  100. const std::string& channel_name,
  101. const ExtensionMsg_TabConnectionInfo& source,
  102. const ExtensionMsg_ExternalConnectionInfo& info,
  103. content::RenderFrame* restrict_to_render_frame) {
  104. DCHECK(!target_port_id.is_opener);
  105. int routing_id = restrict_to_render_frame
  106. ? restrict_to_render_frame->GetRoutingID()
  107. : MSG_ROUTING_NONE;
  108. bool port_created = false;
  109. context_set->ForEach(
  110. info.target_id, restrict_to_render_frame,
  111. base::BindRepeating(
  112. &NativeRendererMessagingService::DispatchOnConnectToScriptContext,
  113. base::Unretained(this), target_port_id, channel_name, &source, info,
  114. &port_created));
  115. // Note: |restrict_to_render_frame| may have been deleted at this point!
  116. IPCMessageSender* ipc_sender = bindings_system_->GetIPCMessageSender();
  117. if (port_created) {
  118. ipc_sender->SendOpenMessagePort(routing_id, target_port_id);
  119. } else {
  120. ipc_sender->SendCloseMessagePort(routing_id, target_port_id, false);
  121. }
  122. }
  123. void NativeRendererMessagingService::DeliverMessage(
  124. ScriptContextSetIterable* context_set,
  125. const PortId& target_port_id,
  126. const Message& message,
  127. content::RenderFrame* restrict_to_render_frame) {
  128. context_set->ForEach(
  129. restrict_to_render_frame,
  130. base::BindRepeating(
  131. &NativeRendererMessagingService::DeliverMessageToScriptContext,
  132. base::Unretained(this), message, target_port_id));
  133. }
  134. void NativeRendererMessagingService::DispatchOnDisconnect(
  135. ScriptContextSetIterable* context_set,
  136. const PortId& port_id,
  137. const std::string& error_message,
  138. content::RenderFrame* restrict_to_render_frame) {
  139. context_set->ForEach(
  140. restrict_to_render_frame,
  141. base::BindRepeating(
  142. &NativeRendererMessagingService::DispatchOnDisconnectToScriptContext,
  143. base::Unretained(this), port_id, error_message));
  144. }
  145. gin::Handle<GinPort> NativeRendererMessagingService::Connect(
  146. ScriptContext* script_context,
  147. const MessageTarget& target,
  148. const std::string& channel_name,
  149. SerializationFormat format) {
  150. if (!ScriptContextIsValid(script_context))
  151. return gin::Handle<GinPort>();
  152. MessagingPerContextData* data = GetPerContextData<MessagingPerContextData>(
  153. script_context->v8_context(), kCreateIfMissing);
  154. if (!data)
  155. return gin::Handle<GinPort>();
  156. bool is_opener = true;
  157. gin::Handle<GinPort> port =
  158. CreatePort(script_context, channel_name,
  159. PortId(script_context->context_id(), data->next_port_id++,
  160. is_opener, format));
  161. bindings_system_->GetIPCMessageSender()->SendOpenMessageChannel(
  162. script_context, port->port_id(), target, channel_name);
  163. return port;
  164. }
  165. v8::Local<v8::Promise> NativeRendererMessagingService::SendOneTimeMessage(
  166. ScriptContext* script_context,
  167. const MessageTarget& target,
  168. const std::string& method_name,
  169. const Message& message,
  170. binding::AsyncResponseType async_type,
  171. v8::Local<v8::Function> response_callback) {
  172. if (!ScriptContextIsValid(script_context))
  173. return v8::Local<v8::Promise>();
  174. MessagingPerContextData* data = GetPerContextData<MessagingPerContextData>(
  175. script_context->v8_context(), kCreateIfMissing);
  176. bool is_opener = true;
  177. // TODO(crbug.com/248548): Instead of inferring the SerializationFormat from
  178. // Message, it'd be better to have the clients pass it directly. This is
  179. // because, in case of `kStructuredCloned` to `kJson` fallback, the format for
  180. // the ports will also be `kJson`. This is inconsistent with what we do for
  181. // ports for long-lived channels where the port's `SerializationFormat` is
  182. // always the same as that passed by messaging clients and is independent of
  183. // any fallback behavior.
  184. PortId port_id(script_context->context_id(), data->next_port_id++, is_opener,
  185. message.format);
  186. return one_time_message_handler_.SendMessage(script_context, port_id, target,
  187. method_name, message, async_type,
  188. response_callback);
  189. }
  190. void NativeRendererMessagingService::PostMessageToPort(
  191. v8::Local<v8::Context> context,
  192. const PortId& port_id,
  193. int routing_id,
  194. std::unique_ptr<Message> message) {
  195. ScriptContext* script_context = GetScriptContextFromV8Context(context);
  196. CHECK(script_context);
  197. if (!ScriptContextIsValid(script_context))
  198. return;
  199. bindings_system_->GetIPCMessageSender()->SendPostMessageToPort(port_id,
  200. *message);
  201. }
  202. void NativeRendererMessagingService::ClosePort(v8::Local<v8::Context> context,
  203. const PortId& port_id,
  204. int routing_id) {
  205. ScriptContext* script_context = GetScriptContextFromV8Context(context);
  206. CHECK(script_context);
  207. MessagingPerContextData* data = GetPerContextData<MessagingPerContextData>(
  208. script_context->v8_context(), kDontCreateIfMissing);
  209. if (!data)
  210. return;
  211. size_t erased = data->ports.erase(port_id);
  212. DCHECK_GT(erased, 0u);
  213. if (!ScriptContextIsValid(script_context))
  214. return;
  215. bool close_channel = true;
  216. bindings_system_->GetIPCMessageSender()->SendCloseMessagePort(
  217. routing_id, port_id, close_channel);
  218. }
  219. gin::Handle<GinPort> NativeRendererMessagingService::CreatePortForTesting(
  220. ScriptContext* script_context,
  221. const std::string& channel_name,
  222. const PortId& port_id) {
  223. return CreatePort(script_context, channel_name, port_id);
  224. }
  225. gin::Handle<GinPort> NativeRendererMessagingService::GetPortForTesting(
  226. ScriptContext* script_context,
  227. const PortId& port_id) {
  228. return GetPort(script_context, port_id);
  229. }
  230. bool NativeRendererMessagingService::HasPortForTesting(
  231. ScriptContext* script_context,
  232. const PortId& port_id) {
  233. return ContextHasMessagePort(script_context, port_id);
  234. }
  235. void NativeRendererMessagingService::ValidateMessagePortInContext(
  236. const PortId& port_id,
  237. bool* has_port,
  238. ScriptContext* script_context) {
  239. if (*has_port)
  240. return; // Stop checking if the port was found.
  241. // No need for |=; we know this is false right now from above.
  242. *has_port = ContextHasMessagePort(script_context, port_id);
  243. }
  244. void NativeRendererMessagingService::DispatchOnConnectToScriptContext(
  245. const PortId& target_port_id,
  246. const std::string& channel_name,
  247. const ExtensionMsg_TabConnectionInfo* source,
  248. const ExtensionMsg_ExternalConnectionInfo& info,
  249. bool* port_created,
  250. ScriptContext* script_context) {
  251. // If the channel was opened by this same context, ignore it. This should only
  252. // happen when messages are sent to an entire process (rather than a single
  253. // frame) as an optimization; otherwise the browser process filters this out.
  254. if (script_context->context_id() == target_port_id.context_id)
  255. return;
  256. // First, determine the event we'll use to connect.
  257. std::string target_extension_id = script_context->GetExtensionID();
  258. bool is_external =
  259. (info.source_endpoint.type == MessagingEndpoint::Type::kExtension ||
  260. info.source_endpoint.type == MessagingEndpoint::Type::kTab) &&
  261. info.source_endpoint.extension_id != target_extension_id;
  262. std::string event_name;
  263. if (info.source_endpoint.type == MessagingEndpoint::Type::kNativeApp) {
  264. event_name = messaging_util::kOnConnectNativeEvent;
  265. } else if (channel_name == messaging_util::kSendRequestChannel) {
  266. event_name = is_external ? messaging_util::kOnRequestExternalEvent
  267. : messaging_util::kOnRequestEvent;
  268. } else if (channel_name == messaging_util::kSendMessageChannel) {
  269. event_name = is_external ? messaging_util::kOnMessageExternalEvent
  270. : messaging_util::kOnMessageEvent;
  271. } else {
  272. event_name = is_external ? messaging_util::kOnConnectExternalEvent
  273. : messaging_util::kOnConnectEvent;
  274. }
  275. // If there are no listeners for the given event, then we know the port won't
  276. // be used in this context.
  277. if (!bindings_system_->HasEventListenerInContext(event_name,
  278. script_context)) {
  279. return;
  280. }
  281. *port_created = true;
  282. DispatchOnConnectToListeners(script_context, target_port_id,
  283. target_extension_id, channel_name, source, info,
  284. event_name);
  285. }
  286. void NativeRendererMessagingService::DeliverMessageToScriptContext(
  287. const Message& message,
  288. const PortId& target_port_id,
  289. ScriptContext* script_context) {
  290. if (!ContextHasMessagePort(script_context, target_port_id))
  291. return;
  292. if (script_context->IsForServiceWorker()) {
  293. DeliverMessageToWorker(message, target_port_id, script_context);
  294. } else {
  295. DeliverMessageToBackgroundPage(message, target_port_id, script_context);
  296. }
  297. }
  298. void NativeRendererMessagingService::DeliverMessageToWorker(
  299. const Message& message,
  300. const PortId& target_port_id,
  301. ScriptContext* script_context) {
  302. // Note |scoped_extension_interaction| requires a HandleScope.
  303. v8::Isolate* isolate = script_context->isolate();
  304. v8::HandleScope handle_scope(isolate);
  305. std::unique_ptr<InteractionProvider::Scope> scoped_extension_interaction;
  306. if (message.user_gesture) {
  307. // TODO(https://crbug.com/977629): Add logging for privilege level for
  308. // sender and receiver and decide if want to allow unprivileged to
  309. // privileged support.
  310. scoped_extension_interaction =
  311. ExtensionInteractionProvider::Scope::ForWorker(
  312. script_context->v8_context());
  313. }
  314. DispatchOnMessageToListeners(script_context, message, target_port_id);
  315. }
  316. void NativeRendererMessagingService::DeliverMessageToBackgroundPage(
  317. const Message& message,
  318. const PortId& target_port_id,
  319. ScriptContext* script_context) {
  320. std::unique_ptr<blink::WebScopedWindowFocusAllowedIndicator>
  321. allow_window_focus;
  322. if (message.user_gesture && script_context->web_frame()) {
  323. bool sender_is_privileged = message.from_privileged_context;
  324. bool receiver_is_privileged =
  325. script_context->context_type() ==
  326. extensions::Feature::BLESSED_EXTENSION_CONTEXT;
  327. UserActivationNotificationType notification_type;
  328. if (sender_is_privileged && receiver_is_privileged) {
  329. notification_type =
  330. UserActivationNotificationType::kExtensionMessagingBothPrivileged;
  331. } else if (sender_is_privileged && !receiver_is_privileged) {
  332. notification_type =
  333. UserActivationNotificationType::kExtensionMessagingSenderPrivileged;
  334. } else if (!sender_is_privileged && receiver_is_privileged) {
  335. notification_type =
  336. UserActivationNotificationType::kExtensionMessagingReceiverPrivileged;
  337. } else /* !sender_is_privileged && !receiver_is_privileged */ {
  338. notification_type =
  339. UserActivationNotificationType::kExtensionMessagingNeitherPrivileged;
  340. }
  341. script_context->web_frame()->NotifyUserActivation(notification_type);
  342. blink::WebDocument document = script_context->web_frame()->GetDocument();
  343. allow_window_focus =
  344. std::make_unique<blink::WebScopedWindowFocusAllowedIndicator>(
  345. &document);
  346. }
  347. DispatchOnMessageToListeners(script_context, message, target_port_id);
  348. }
  349. void NativeRendererMessagingService::DispatchOnDisconnectToScriptContext(
  350. const PortId& port_id,
  351. const std::string& error_message,
  352. ScriptContext* script_context) {
  353. if (!ContextHasMessagePort(script_context, port_id))
  354. return;
  355. DispatchOnDisconnectToListeners(script_context, port_id, error_message);
  356. }
  357. bool NativeRendererMessagingService::ContextHasMessagePort(
  358. ScriptContext* script_context,
  359. const PortId& port_id) {
  360. if (one_time_message_handler_.HasPort(script_context, port_id))
  361. return true;
  362. v8::HandleScope handle_scope(script_context->isolate());
  363. MessagingPerContextData* data = GetPerContextData<MessagingPerContextData>(
  364. script_context->v8_context(), kDontCreateIfMissing);
  365. return data && base::Contains(data->ports, port_id);
  366. }
  367. void NativeRendererMessagingService::DispatchOnConnectToListeners(
  368. ScriptContext* script_context,
  369. const PortId& target_port_id,
  370. const ExtensionId& target_extension_id,
  371. const std::string& channel_name,
  372. const ExtensionMsg_TabConnectionInfo* source,
  373. const ExtensionMsg_ExternalConnectionInfo& info,
  374. const std::string& event_name) {
  375. v8::Isolate* isolate = script_context->isolate();
  376. v8::HandleScope handle_scope(isolate);
  377. v8::Local<v8::Context> v8_context = script_context->v8_context();
  378. v8::Context::Scope context_scope(v8_context);
  379. gin::DataObjectBuilder sender_builder(isolate);
  380. if (info.source_endpoint.extension_id)
  381. sender_builder.Set("id", *info.source_endpoint.extension_id);
  382. if (info.source_endpoint.native_app_name) {
  383. sender_builder.Set("nativeApplication",
  384. *info.source_endpoint.native_app_name);
  385. }
  386. if (!info.source_url.is_empty())
  387. sender_builder.Set("url", info.source_url.spec());
  388. if (info.source_origin)
  389. sender_builder.Set("origin", info.source_origin->Serialize());
  390. if (source->frame_id >= 0)
  391. sender_builder.Set("frameId", source->frame_id);
  392. if (!source->document_id.empty())
  393. sender_builder.Set("documentId", source->document_id);
  394. if (!source->document_lifecycle.empty())
  395. sender_builder.Set("documentLifecycle", source->document_lifecycle);
  396. const Extension* extension = script_context->extension();
  397. if (extension) {
  398. if (!source->tab.DictEmpty() && !extension->is_platform_app()) {
  399. sender_builder.Set("tab", content::V8ValueConverter::Create()->ToV8Value(
  400. source->tab, v8_context));
  401. }
  402. ExternallyConnectableInfo* externally_connectable =
  403. ExternallyConnectableInfo::Get(extension);
  404. if (externally_connectable &&
  405. externally_connectable->accepts_tls_channel_id) {
  406. sender_builder.Set("tlsChannelId", std::string());
  407. }
  408. if (info.guest_process_id != content::ChildProcessHost::kInvalidUniqueID) {
  409. CHECK(Manifest::IsComponentLocation(extension->location()))
  410. << "GuestProcessId can only be exposed to component extensions.";
  411. sender_builder.Set("guestProcessId", info.guest_process_id)
  412. .Set("guestRenderFrameRoutingId", info.guest_render_frame_routing_id);
  413. }
  414. }
  415. v8::Local<v8::Object> sender = sender_builder.Build();
  416. if (channel_name == "chrome.extension.sendRequest" ||
  417. channel_name == "chrome.runtime.sendMessage") {
  418. one_time_message_handler_.AddReceiver(script_context, target_port_id,
  419. sender, event_name);
  420. } else {
  421. gin::Handle<GinPort> port =
  422. CreatePort(script_context, channel_name, target_port_id);
  423. port->SetSender(v8_context, sender);
  424. std::vector<v8::Local<v8::Value>> args = {port.ToV8()};
  425. bindings_system_->api_system()->event_handler()->FireEventInContext(
  426. event_name, v8_context, &args, nullptr, JSRunner::ResultCallback());
  427. }
  428. // Note: Arbitrary JS may have run; the context may now be deleted.
  429. if (binding::IsContextValid(v8_context) &&
  430. APIActivityLogger::IsLoggingEnabled()) {
  431. base::Value::List list;
  432. list.reserve(2u);
  433. if (info.source_endpoint.extension_id)
  434. list.Append(*info.source_endpoint.extension_id);
  435. else if (info.source_endpoint.native_app_name)
  436. list.Append(*info.source_endpoint.native_app_name);
  437. else
  438. list.Append(base::Value());
  439. if (!info.source_url.is_empty())
  440. list.Append(info.source_url.spec());
  441. else
  442. list.Append(base::Value());
  443. APIActivityLogger::LogEvent(bindings_system_->GetIPCMessageSender(),
  444. script_context, event_name, std::move(list));
  445. }
  446. }
  447. void NativeRendererMessagingService::DispatchOnMessageToListeners(
  448. ScriptContext* script_context,
  449. const Message& message,
  450. const PortId& target_port_id) {
  451. v8::Isolate* isolate = script_context->isolate();
  452. v8::HandleScope handle_scope(isolate);
  453. v8::Context::Scope context_scope(script_context->v8_context());
  454. if (one_time_message_handler_.DeliverMessage(script_context, message,
  455. target_port_id)) {
  456. return;
  457. }
  458. gin::Handle<GinPort> port = GetPort(script_context, target_port_id);
  459. DCHECK(!port.IsEmpty());
  460. port->DispatchOnMessage(script_context->v8_context(), message);
  461. // Note: Arbitrary JS may have run; the context may now be deleted.
  462. }
  463. void NativeRendererMessagingService::DispatchOnDisconnectToListeners(
  464. ScriptContext* script_context,
  465. const PortId& port_id,
  466. const std::string& error_message) {
  467. v8::Isolate* isolate = script_context->isolate();
  468. v8::HandleScope handle_scope(isolate);
  469. v8::Local<v8::Context> v8_context = script_context->v8_context();
  470. v8::Context::Scope context_scope(v8_context);
  471. if (one_time_message_handler_.Disconnect(script_context, port_id,
  472. error_message)) {
  473. return;
  474. }
  475. gin::Handle<GinPort> port = GetPort(script_context, port_id);
  476. DCHECK(!port.IsEmpty());
  477. if (!error_message.empty()) {
  478. // TODO(devlin): Subtle: If the JS event to disconnect the port happens
  479. // asynchronously because JS is suspended, this last error won't be
  480. // correctly set for listeners. Given this exceedingly rare, and shouldn't
  481. // behave too strangely, this is somewhat low priority.
  482. bindings_system_->api_system()->request_handler()->last_error()->SetError(
  483. v8_context, error_message);
  484. }
  485. port->DispatchOnDisconnect(v8_context);
  486. // Note: Arbitrary JS may have run; the context may now be deleted.
  487. if (!binding::IsContextValid(v8_context))
  488. return;
  489. if (!error_message.empty()) {
  490. bindings_system_->api_system()->request_handler()->last_error()->ClearError(
  491. v8_context, true);
  492. }
  493. MessagingPerContextData* data = GetPerContextData<MessagingPerContextData>(
  494. v8_context, kDontCreateIfMissing);
  495. DCHECK(data);
  496. data->ports.erase(port_id);
  497. }
  498. gin::Handle<GinPort> NativeRendererMessagingService::CreatePort(
  499. ScriptContext* script_context,
  500. const std::string& channel_name,
  501. const PortId& port_id) {
  502. // Note: no HandleScope because it would invalidate the gin::Handle::wrapper_.
  503. v8::Isolate* isolate = script_context->isolate();
  504. v8::Local<v8::Context> context = script_context->v8_context();
  505. // Note: needed because gin::CreateHandle infers the context from the active
  506. // context on the isolate.
  507. v8::Context::Scope context_scope(context);
  508. // If this port is an opener, then it should have been created in this
  509. // context. Otherwise, it should have been created in another context, because
  510. // we don't support intra-context message passing.
  511. if (port_id.is_opener)
  512. DCHECK_EQ(port_id.context_id, script_context->context_id());
  513. else
  514. DCHECK_NE(port_id.context_id, script_context->context_id());
  515. content::RenderFrame* render_frame = script_context->GetRenderFrame();
  516. int routing_id =
  517. render_frame ? render_frame->GetRoutingID() : MSG_ROUTING_NONE;
  518. MessagingPerContextData* data =
  519. GetPerContextData<MessagingPerContextData>(context, kCreateIfMissing);
  520. DCHECK(data);
  521. DCHECK(!base::Contains(data->ports, port_id));
  522. gin::Handle<GinPort> port_handle = gin::CreateHandle(
  523. isolate,
  524. new GinPort(context, port_id, routing_id, channel_name,
  525. bindings_system_->api_system()->event_handler(), this));
  526. v8::Local<v8::Object> port_object = port_handle.ToV8().As<v8::Object>();
  527. data->ports[port_id].Reset(isolate, port_object);
  528. return port_handle;
  529. }
  530. gin::Handle<GinPort> NativeRendererMessagingService::GetPort(
  531. ScriptContext* script_context,
  532. const PortId& port_id) {
  533. // Note: no HandleScope because it would invalidate the gin::Handle::wrapper_.
  534. v8::Isolate* isolate = script_context->isolate();
  535. v8::Local<v8::Context> context = script_context->v8_context();
  536. MessagingPerContextData* data = GetPerContextData<MessagingPerContextData>(
  537. script_context->v8_context(), kDontCreateIfMissing);
  538. DCHECK(data);
  539. DCHECK(base::Contains(data->ports, port_id));
  540. GinPort* port = nullptr;
  541. gin::Converter<GinPort*>::FromV8(context->GetIsolate(),
  542. data->ports[port_id].Get(isolate), &port);
  543. CHECK(port);
  544. return gin::CreateHandle(isolate, port);
  545. }
  546. } // namespace extensions