MessagePort.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /*
  2. * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibCore/Socket.h>
  7. #include <LibCore/System.h>
  8. #include <LibIPC/File.h>
  9. #include <LibWeb/Bindings/ExceptionOrUtils.h>
  10. #include <LibWeb/Bindings/Intrinsics.h>
  11. #include <LibWeb/Bindings/MessagePortPrototype.h>
  12. #include <LibWeb/DOM/EventDispatcher.h>
  13. #include <LibWeb/HTML/EventHandler.h>
  14. #include <LibWeb/HTML/EventLoop/EventLoop.h>
  15. #include <LibWeb/HTML/EventNames.h>
  16. #include <LibWeb/HTML/MessageEvent.h>
  17. #include <LibWeb/HTML/MessagePort.h>
  18. #include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
  19. namespace Web::HTML {
  20. constexpr u8 IPC_FILE_TAG = 0xA5;
  21. JS_DEFINE_ALLOCATOR(MessagePort);
  22. JS::NonnullGCPtr<MessagePort> MessagePort::create(JS::Realm& realm)
  23. {
  24. return realm.heap().allocate<MessagePort>(realm, realm);
  25. }
  26. MessagePort::MessagePort(JS::Realm& realm)
  27. : DOM::EventTarget(realm)
  28. {
  29. }
  30. MessagePort::~MessagePort() = default;
  31. void MessagePort::initialize(JS::Realm& realm)
  32. {
  33. Base::initialize(realm);
  34. set_prototype(&Bindings::ensure_web_prototype<Bindings::MessagePortPrototype>(realm, "MessagePort"_fly_string));
  35. }
  36. void MessagePort::visit_edges(Cell::Visitor& visitor)
  37. {
  38. Base::visit_edges(visitor);
  39. visitor.visit(m_remote_port);
  40. }
  41. // https://html.spec.whatwg.org/multipage/web-messaging.html#message-ports:transfer-steps
  42. WebIDL::ExceptionOr<void> MessagePort::transfer_steps(HTML::TransferDataHolder& data_holder)
  43. {
  44. // 1. Set value's has been shipped flag to true.
  45. m_has_been_shipped = true;
  46. // FIXME: 2. Set dataHolder.[[PortMessageQueue]] to value's port message queue.
  47. // FIXME: Support delivery of messages that haven't been delivered yet on the other side
  48. // 3. If value is entangled with another port remotePort, then:
  49. if (is_entangled()) {
  50. // 1. Set remotePort's has been shipped flag to true.
  51. m_remote_port->m_has_been_shipped = true;
  52. // 2. Set dataHolder.[[RemotePort]] to remotePort.
  53. auto fd = MUST(m_socket->release_fd());
  54. m_socket = nullptr;
  55. data_holder.fds.append(fd);
  56. data_holder.data.append(IPC_FILE_TAG);
  57. }
  58. // 4. Otherwise, set dataHolder.[[RemotePort]] to null.
  59. else {
  60. data_holder.data.append(0);
  61. }
  62. return {};
  63. }
  64. WebIDL::ExceptionOr<void> MessagePort::transfer_receiving_steps(HTML::TransferDataHolder& data_holder)
  65. {
  66. // 1. Set value's has been shipped flag to true.
  67. m_has_been_shipped = true;
  68. // FIXME 2. Move all the tasks that are to fire message events in dataHolder.[[PortMessageQueue]] to the port message queue of value,
  69. // if any, leaving value's port message queue in its initial disabled state, and, if value's relevant global object is a Window,
  70. // associating the moved tasks with value's relevant global object's associated Document.
  71. // 3. If dataHolder.[[RemotePort]] is not null, then entangle dataHolder.[[RemotePort]] and value.
  72. // (This will disentangle dataHolder.[[RemotePort]] from the original port that was transferred.)
  73. auto fd_tag = data_holder.data.take_first();
  74. if (fd_tag == IPC_FILE_TAG) {
  75. auto fd = data_holder.fds.take_first();
  76. m_socket = MUST(Core::LocalSocket::adopt_fd(fd.take_fd()));
  77. } else if (fd_tag != 0) {
  78. dbgln("Unexpected byte {:x} in MessagePort transfer data", fd_tag);
  79. VERIFY_NOT_REACHED();
  80. }
  81. return {};
  82. }
  83. void MessagePort::disentangle()
  84. {
  85. m_remote_port->m_remote_port = nullptr;
  86. m_remote_port = nullptr;
  87. m_socket = nullptr;
  88. }
  89. // https://html.spec.whatwg.org/multipage/web-messaging.html#entangle
  90. void MessagePort::entangle_with(MessagePort& remote_port)
  91. {
  92. if (m_remote_port.ptr() == &remote_port)
  93. return;
  94. // 1. If one of the ports is already entangled, then disentangle it and the port that it was entangled with.
  95. if (is_entangled())
  96. disentangle();
  97. if (remote_port.is_entangled())
  98. remote_port.disentangle();
  99. // 2. Associate the two ports to be entangled, so that they form the two parts of a new channel.
  100. // (There is no MessageChannel object that represents this channel.)
  101. remote_port.m_remote_port = this;
  102. m_remote_port = &remote_port;
  103. int fds[2] = {};
  104. MUST(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, fds));
  105. auto socket0 = MUST(Core::LocalSocket::adopt_fd(fds[0]));
  106. MUST(socket0->set_blocking(false));
  107. MUST(socket0->set_close_on_exec(true));
  108. auto socket1 = MUST(Core::LocalSocket::adopt_fd(fds[1]));
  109. MUST(socket1->set_blocking(false));
  110. MUST(socket1->set_close_on_exec(true));
  111. m_socket = move(socket0);
  112. m_remote_port->m_socket = move(socket1);
  113. }
  114. // https://html.spec.whatwg.org/multipage/web-messaging.html#dom-messageport-postmessage-options
  115. WebIDL::ExceptionOr<void> MessagePort::post_message(JS::Value message, Vector<JS::Handle<JS::Object>> const& transfer)
  116. {
  117. // 1. Let targetPort be the port with which this MessagePort is entangled, if any; otherwise let it be null.
  118. JS::GCPtr<MessagePort> target_port = m_remote_port;
  119. // 2. Let options be «[ "transfer" → transfer ]».
  120. auto options = StructuredSerializeOptions { transfer };
  121. // 3. Run the message port post message steps providing this, targetPort, message and options.
  122. return message_port_post_message_steps(target_port, message, options);
  123. }
  124. // https://html.spec.whatwg.org/multipage/web-messaging.html#dom-messageport-postmessage
  125. WebIDL::ExceptionOr<void> MessagePort::post_message(JS::Value message, StructuredSerializeOptions const& options)
  126. {
  127. // 1. Let targetPort be the port with which this MessagePort is entangled, if any; otherwise let it be null.
  128. JS::GCPtr<MessagePort> target_port = m_remote_port;
  129. // 2. Run the message port post message steps providing targetPort, message and options.
  130. return message_port_post_message_steps(target_port, message, options);
  131. }
  132. // https://html.spec.whatwg.org/multipage/web-messaging.html#message-port-post-message-steps
  133. WebIDL::ExceptionOr<void> MessagePort::message_port_post_message_steps(JS::GCPtr<MessagePort> target_port, JS::Value message, StructuredSerializeOptions const& options)
  134. {
  135. auto& realm = this->realm();
  136. auto& vm = this->vm();
  137. // 1. Let transfer be options["transfer"].
  138. auto const& transfer = options.transfer;
  139. // 2. If transfer contains this MessagePort, then throw a "DataCloneError" DOMException.
  140. for (auto const& handle : transfer) {
  141. if (handle == this)
  142. return WebIDL::DataCloneError::create(realm, "Cannot transfer a MessagePort to itself"_fly_string);
  143. }
  144. // 3. Let doomed be false.
  145. bool doomed = false;
  146. // 4. If targetPort is not null and transfer contains targetPort, then set doomed to true and optionally report to a developer console that the target port was posted to itself, causing the communication channel to be lost.
  147. if (target_port) {
  148. for (auto const& handle : transfer) {
  149. if (handle == target_port.ptr()) {
  150. doomed = true;
  151. dbgln("FIXME: Report to a developer console that the target port was posted to itself, causing the communication channel to be lost");
  152. }
  153. }
  154. }
  155. // 5. Let serializeWithTransferResult be StructuredSerializeWithTransfer(message, transfer). Rethrow any exceptions.
  156. auto serialize_with_transfer_result = TRY(structured_serialize_with_transfer(vm, message, transfer));
  157. // 6. If targetPort is null, or if doomed is true, then return.
  158. if (!target_port || doomed)
  159. return {};
  160. // FIXME: 7. Add a task that runs the following steps to the port message queue of targetPort:
  161. // FIXME: Implement this using the port message queue/unshipped port message queue concept
  162. main_thread_event_loop().task_queue().add(HTML::Task::create(HTML::Task::Source::PostedMessage, nullptr, [target_port, serialize_with_transfer_result = move(serialize_with_transfer_result)]() mutable {
  163. // FIXME: 1. Let finalTargetPort be the MessagePort in whose port message queue the task now finds itself.
  164. // FIXME: NOTE: This can be different from targetPort, if targetPort itself was transferred and thus all its tasks moved along with it.
  165. auto final_target_port = target_port;
  166. // 2. Let targetRealm be finalTargetPort's relevant realm.
  167. auto& target_realm = relevant_realm(*final_target_port);
  168. auto& target_vm = target_realm.vm();
  169. // 3. Let deserializeRecord be StructuredDeserializeWithTransfer(serializeWithTransferResult, targetRealm).
  170. TemporaryExecutionContext context { relevant_settings_object(*final_target_port) };
  171. auto deserialize_record_or_error = structured_deserialize_with_transfer(target_vm, serialize_with_transfer_result);
  172. if (deserialize_record_or_error.is_error()) {
  173. // If this throws an exception, catch it, fire an event named messageerror at finalTargetPort, using MessageEvent, and then return.
  174. auto exception = deserialize_record_or_error.release_error();
  175. MessageEventInit event_init {};
  176. final_target_port->dispatch_event(MessageEvent::create(target_realm, HTML::EventNames::messageerror, event_init));
  177. return;
  178. }
  179. auto deserialize_record = deserialize_record_or_error.release_value();
  180. // 4. Let messageClone be deserializeRecord.[[Deserialized]].
  181. auto message_clone = deserialize_record.deserialized;
  182. // 5. Let newPorts be a new frozen array consisting of all MessagePort objects in deserializeRecord.[[TransferredValues]], if any, maintaining their relative order.
  183. // FIXME: Use a FrozenArray
  184. Vector<JS::Handle<JS::Object>> new_ports;
  185. for (auto const& object : deserialize_record.transferred_values) {
  186. if (is<HTML::MessagePort>(*object)) {
  187. new_ports.append(object);
  188. }
  189. }
  190. // 6. Fire an event named message at finalTargetPort, using MessageEvent, with the data attribute initialized to messageClone and the ports attribute initialized to newPorts.
  191. MessageEventInit event_init {};
  192. event_init.data = message_clone;
  193. event_init.ports = move(new_ports);
  194. final_target_port->dispatch_event(MessageEvent::create(target_realm, HTML::EventNames::message, event_init));
  195. }));
  196. return {};
  197. }
  198. // https://html.spec.whatwg.org/multipage/web-messaging.html#dom-messageport-start
  199. void MessagePort::start()
  200. {
  201. VERIFY(m_socket);
  202. // TODO: The start() method steps are to enable this's port message queue, if it is not already enabled.
  203. }
  204. // https://html.spec.whatwg.org/multipage/web-messaging.html#dom-messageport-close
  205. void MessagePort::close()
  206. {
  207. // 1. Set this MessagePort object's [[Detached]] internal slot value to true.
  208. set_detached(true);
  209. // 2. If this MessagePort object is entangled, disentangle it.
  210. if (is_entangled())
  211. disentangle();
  212. }
  213. #undef __ENUMERATE
  214. #define __ENUMERATE(attribute_name, event_name) \
  215. void MessagePort::set_##attribute_name(WebIDL::CallbackType* value) \
  216. { \
  217. set_event_handler_attribute(event_name, value); \
  218. } \
  219. WebIDL::CallbackType* MessagePort::attribute_name() \
  220. { \
  221. return event_handler_attribute(event_name); \
  222. }
  223. ENUMERATE_MESSAGE_PORT_EVENT_HANDLERS(__ENUMERATE)
  224. #undef __ENUMERATE
  225. }