diff options
author | Andreas Kling <kling@serenityos.org> | 2021-09-19 22:12:31 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-09-19 22:34:44 +0200 |
commit | 95559c427704b00ea3b7adbd7df44f4ba53f50f9 (patch) | |
tree | 622ad62ca20404c4da9b67c658348cacc6cc41bf | |
parent | c7aa32b90fda6f3720f99bd3368576982a635e9c (diff) | |
download | serenity-95559c427704b00ea3b7adbd7df44f4ba53f50f9.zip |
LibWeb: Implement basic support for MessageChannel and MessagePort
This patch adds a basic initial implementation of these API's.
Since LibWeb currently doesn't support workers, this implementation of
messaging doesn't bother with serializing and deserializing messages.
-rw-r--r-- | Base/res/html/misc/message-channel.html | 7 | ||||
-rw-r--r-- | Base/res/html/misc/welcome.html | 1 | ||||
-rw-r--r-- | Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CMakeLists.txt | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Forward.h | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/MessageChannel.cpp | 31 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/MessageChannel.h | 47 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/MessageChannel.idl | 8 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/MessagePort.cpp | 126 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/MessagePort.h | 79 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/MessagePort.idl | 10 |
12 files changed, 322 insertions, 0 deletions
diff --git a/Base/res/html/misc/message-channel.html b/Base/res/html/misc/message-channel.html new file mode 100644 index 0000000000..6ee38fc899 --- /dev/null +++ b/Base/res/html/misc/message-channel.html @@ -0,0 +1,7 @@ +<script> +var channel = new MessageChannel(); +channel.port2.onmessage = function(event) { + console.log("Port2 received '" + event.data + "'"); +} +channel.port1.postMessage("HELLO FRIEND"); +</script> diff --git a/Base/res/html/misc/welcome.html b/Base/res/html/misc/welcome.html index c6f410b2bd..c3abeb56fe 100644 --- a/Base/res/html/misc/welcome.html +++ b/Base/res/html/misc/welcome.html @@ -127,6 +127,7 @@ <h2>JavaScript/Wasm</h2> <ul> + <li><a href="message-channel.html">MessageChannel</a></li> <li><a href="websocket.html">WebSocket API Test</a></li> <li><a href="cookie.html">document.cookie</a></li> <li><a href="event-bubbling-and-multiple-listeners.html">event bubbling and multiple listeners</a></li> diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp index 76b65dc180..9e48eb8ec8 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp @@ -1089,6 +1089,7 @@ void generate_implementation(IDL::Interface const& interface) #include <LibWeb/Bindings/HTMLTableCaptionElementWrapper.h> #include <LibWeb/Bindings/HTMLTableSectionElementWrapper.h> #include <LibWeb/Bindings/ImageDataWrapper.h> +#include <LibWeb/Bindings/MessagePortWrapper.h> #include <LibWeb/Bindings/NodeWrapperFactory.h> #include <LibWeb/Bindings/TextWrapper.h> #include <LibWeb/Bindings/WindowObject.h> @@ -1482,6 +1483,7 @@ void generate_prototype_implementation(IDL::Interface const& interface) #include <LibWeb/Bindings/HTMLTableSectionElementWrapper.h> #include <LibWeb/Bindings/ImageDataWrapper.h> #include <LibWeb/Bindings/LocationObject.h> +#include <LibWeb/Bindings/MessagePortWrapper.h> #include <LibWeb/Bindings/NodeWrapperFactory.h> #include <LibWeb/Bindings/PerformanceTimingWrapper.h> #include <LibWeb/Bindings/RangeWrapper.h> diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h index a16f1883a9..7427e0f604 100644 --- a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h +++ b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h @@ -189,6 +189,8 @@ #include <LibWeb/Bindings/ImageDataPrototype.h> #include <LibWeb/Bindings/MediaQueryListConstructor.h> #include <LibWeb/Bindings/MediaQueryListPrototype.h> +#include <LibWeb/Bindings/MessageChannelConstructor.h> +#include <LibWeb/Bindings/MessageChannelPrototype.h> #include <LibWeb/Bindings/MessageEventConstructor.h> #include <LibWeb/Bindings/MessageEventPrototype.h> #include <LibWeb/Bindings/MouseEventConstructor.h> @@ -341,6 +343,7 @@ ADD_WINDOW_OBJECT_INTERFACE(HTMLVideoElement) \ ADD_WINDOW_OBJECT_INTERFACE(ImageData) \ ADD_WINDOW_OBJECT_INTERFACE(MediaQueryList) \ + ADD_WINDOW_OBJECT_INTERFACE(MessageChannel) \ ADD_WINDOW_OBJECT_INTERFACE(MessageEvent) \ ADD_WINDOW_OBJECT_INTERFACE(MouseEvent) \ ADD_WINDOW_OBJECT_INTERFACE(Node) \ diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 737f925e1c..57b67867f5 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -153,6 +153,8 @@ set(SOURCES HTML/HTMLUnknownElement.cpp HTML/HTMLVideoElement.cpp HTML/ImageData.cpp + HTML/MessageChannel.cpp + HTML/MessagePort.cpp HTML/Parser/Entities.cpp HTML/Parser/HTMLDocumentParser.cpp HTML/Parser/HTMLEncodingDetection.cpp @@ -412,7 +414,9 @@ libweb_js_wrapper(HTML/HTMLUListElement) libweb_js_wrapper(HTML/HTMLUnknownElement) libweb_js_wrapper(HTML/HTMLVideoElement) libweb_js_wrapper(HTML/ImageData) +libweb_js_wrapper(HTML/MessageChannel) libweb_js_wrapper(HTML/MessageEvent) +libweb_js_wrapper(HTML/MessagePort) libweb_js_wrapper(HTML/SubmitEvent) libweb_js_wrapper(HTML/WebSocket) libweb_js_wrapper(HighResolutionTime/Performance) diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index de21054194..c590a24c98 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -141,7 +141,9 @@ class HTMLUListElement; class HTMLUnknownElement; class HTMLVideoElement; class ImageData; +class MessageChannel; class MessageEvent; +class MessagePort; class WebSocket; } @@ -310,7 +312,9 @@ class IdleDeadlineWrapper; class ImageDataWrapper; class LocationObject; class MediaQueryListWrapper; +class MessageChannelWrapper; class MessageEventWrapper; +class MessagePortWrapper; class MouseEventWrapper; class NodeWrapper; class PerformanceTimingWrapper; diff --git a/Userland/Libraries/LibWeb/HTML/MessageChannel.cpp b/Userland/Libraries/LibWeb/HTML/MessageChannel.cpp new file mode 100644 index 0000000000..f45d7c081f --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/MessageChannel.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibWeb/DOM/Document.h> +#include <LibWeb/HTML/MessageChannel.h> +#include <LibWeb/HTML/MessagePort.h> + +namespace Web::HTML { + +MessageChannel::MessageChannel(Bindings::WindowObject& global_object) +{ + auto& context = global_object.impl().associated_document(); + + // 1. Set this's port 1 to a new MessagePort in this's relevant Realm. + m_port1 = MessagePort::create(context); + + // 2. Set this's port 2 to a new MessagePort in this's relevant Realm. + m_port2 = MessagePort::create(context); + + // 3. Entangle this's port 1 and this's port 2. + m_port1->entangle_with({}, *m_port2); +} + +MessageChannel::~MessageChannel() +{ +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/MessageChannel.h b/Userland/Libraries/LibWeb/HTML/MessageChannel.h new file mode 100644 index 0000000000..9abe9b91d1 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/MessageChannel.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/RefCounted.h> +#include <LibWeb/Bindings/Wrappable.h> +#include <LibWeb/DOM/EventTarget.h> +#include <LibWeb/DOM/Window.h> +#include <LibWeb/Forward.h> + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/web-messaging.html#message-channels +class MessageChannel final + : public RefCounted<MessageChannel> + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::MessageChannelWrapper; + + using RefCounted::ref; + using RefCounted::unref; + + static NonnullRefPtr<MessageChannel> create_with_global_object(Bindings::WindowObject& global_object) + { + return adopt_ref(*new MessageChannel(global_object)); + } + + virtual ~MessageChannel() override; + + MessagePort* port1() { return m_port1; } + MessagePort const* port1() const { return m_port1; } + + MessagePort* port2() { return m_port2; } + MessagePort const* port2() const { return m_port2; } + +private: + explicit MessageChannel(Bindings::WindowObject&); + + RefPtr<MessagePort> m_port1; + RefPtr<MessagePort> m_port2; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/MessageChannel.idl b/Userland/Libraries/LibWeb/HTML/MessageChannel.idl new file mode 100644 index 0000000000..22cd3cb5a2 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/MessageChannel.idl @@ -0,0 +1,8 @@ +interface MessageChannel { + + constructor(); + + readonly attribute MessagePort port1; + readonly attribute MessagePort port2; + +}; diff --git a/Userland/Libraries/LibWeb/HTML/MessagePort.cpp b/Userland/Libraries/LibWeb/HTML/MessagePort.cpp new file mode 100644 index 0000000000..f80e943781 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/MessagePort.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibWeb/Bindings/MessagePortWrapper.h> +#include <LibWeb/Bindings/ScriptExecutionContext.h> +#include <LibWeb/DOM/EventDispatcher.h> +#include <LibWeb/HTML/EventHandler.h> +#include <LibWeb/HTML/EventLoop/EventLoop.h> +#include <LibWeb/HTML/EventNames.h> +#include <LibWeb/HTML/MessageEvent.h> +#include <LibWeb/HTML/MessagePort.h> + +namespace Web::HTML { + +MessagePort::MessagePort(Bindings::ScriptExecutionContext& context) + : DOM::EventTarget(context) +{ +} + +MessagePort::~MessagePort() +{ +} + +void MessagePort::disentangle() +{ + m_remote_port->m_remote_port = nullptr; + m_remote_port = nullptr; +} + +// https://html.spec.whatwg.org/multipage/web-messaging.html#entangle +void MessagePort::entangle_with(Badge<MessageChannel>, MessagePort& remote_port) +{ + if (m_remote_port == &remote_port) + return; + + // 1. If one of the ports is already entangled, then disentangle it and the port that it was entangled with. + if (is_entangled()) + disentangle(); + if (remote_port.is_entangled()) + remote_port.disentangle(); + + // 2. Associate the two ports to be entangled, so that they form the two parts of a new channel. + // (There is no MessageChannel object that represents this channel.) + remote_port.m_remote_port = *this; + m_remote_port = &remote_port; +} + +// https://html.spec.whatwg.org/multipage/web-messaging.html#dom-messageport-postmessage +void MessagePort::post_message(JS::Value message) +{ + // 1. Let targetPort be the port with which this MessagePort is entangled, if any; otherwise let it be null. + auto* target_port = m_remote_port.ptr(); + + // FIXME: 2. Let options be ยซ[ "transfer" โ transfer ]ยป. + + // 3. Run the message port post message steps providing targetPort, message and options. + + // https://html.spec.whatwg.org/multipage/web-messaging.html#message-port-post-message-steps + + // FIXME: 1. Let transfer be options["transfer"]. + + // FIXME: 2. If transfer contains this MessagePort, then throw a "DataCloneError" DOMException. + + // 3. Let doomed be false. + bool doomed = false; + + // FIXME: 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. + + // FIXME: 5. Let serializeWithTransferResult be StructuredSerializeWithTransfer(message, transfer). Rethrow any exceptions. + + // 6. If targetPort is null, or if doomed is true, then return. + if (!target_port || doomed) + return; + + // FIXME: 7. Add a task that runs the following steps to the port message queue of targetPort: + + // FIXME: This is an ad-hoc hack implementation instead, since we don't currently + // have serialization and deserialization of messages. + main_thread_event_loop().task_queue().add(HTML::Task::create(HTML::Task::Source::PostedMessage, nullptr, [strong_port = NonnullRefPtr { *target_port }, message]() mutable { + strong_port->dispatch_event(MessageEvent::create(HTML::EventNames::message, message.to_string_without_side_effects(), "<origin>")); + })); +} + +bool MessagePort::dispatch_event(NonnullRefPtr<DOM::Event> event) +{ + return DOM::EventDispatcher::dispatch(*this, move(event)); +} + +JS::Object* MessagePort::create_wrapper(JS::GlobalObject& global_object) +{ + return wrap(global_object, *this); +} + +void MessagePort::start() +{ + // FIXME: Message ports are supposed to be disabled by default. +} + +// https://html.spec.whatwg.org/multipage/web-messaging.html#dom-messageport-close +void MessagePort::close() +{ + // 1. Set this MessagePort object's [[Detached]] internal slot value to true. + m_detached = true; + + // 2. If this MessagePort object is entangled, disentangle it. + if (is_entangled()) + disentangle(); +} + +#undef __ENUMERATE +#define __ENUMERATE(attribute_name, event_name) \ + void MessagePort::set_##attribute_name(HTML::EventHandler value) \ + { \ + set_event_handler_attribute(event_name, move(value)); \ + } \ + HTML::EventHandler MessagePort::attribute_name() \ + { \ + return event_handler_attribute(event_name); \ + } +ENUMERATE_MESSAGE_PORT_EVENT_HANDLERS(__ENUMERATE) +#undef __ENUMERATE + +} diff --git a/Userland/Libraries/LibWeb/HTML/MessagePort.h b/Userland/Libraries/LibWeb/HTML/MessagePort.h new file mode 100644 index 0000000000..18446b8e5f --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/MessagePort.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/RefCounted.h> +#include <AK/Weakable.h> +#include <LibWeb/Bindings/Wrappable.h> +#include <LibWeb/DOM/EventTarget.h> +#include <LibWeb/Forward.h> + +namespace Web::HTML { + +#define ENUMERATE_MESSAGE_PORT_EVENT_HANDLERS(E) \ + E(onmessage, HTML::EventNames::message) \ + E(onmessageerror, HTML::EventNames::messageerror) + +// https://html.spec.whatwg.org/multipage/web-messaging.html#message-ports +class MessagePort final + : public RefCounted<MessagePort> + , public Weakable<MessagePort> + , public DOM::EventTarget + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::MessagePortWrapper; + + using RefCounted::ref; + using RefCounted::unref; + + static NonnullRefPtr<MessagePort> create(Bindings::ScriptExecutionContext& context) + { + return adopt_ref(*new MessagePort(context)); + } + + virtual ~MessagePort() override; + + // ^EventTarget + virtual void ref_event_target() override { ref(); } + virtual void unref_event_target() override { unref(); } + virtual bool dispatch_event(NonnullRefPtr<DOM::Event>) override; + virtual JS::Object* create_wrapper(JS::GlobalObject&) override; + + // https://html.spec.whatwg.org/multipage/web-messaging.html#entangle + void entangle_with(Badge<MessageChannel>, MessagePort&); + + // https://html.spec.whatwg.org/multipage/web-messaging.html#dom-messageport-postmessage + void post_message(JS::Value); + + void start(); + + void close(); + +#undef __ENUMERATE +#define __ENUMERATE(attribute_name, event_name) \ + void set_##attribute_name(HTML::EventHandler); \ + HTML::EventHandler attribute_name(); + ENUMERATE_MESSAGE_PORT_EVENT_HANDLERS(__ENUMERATE) +#undef __ENUMERATE + +private: + explicit MessagePort(Bindings::ScriptExecutionContext&); + + bool is_entangled() const { return m_remote_port; } + void disentangle(); + + // The HTML spec implies(!) that this is MessagePort.[[RemotePort]] + WeakPtr<MessagePort> m_remote_port; + + // https://html.spec.whatwg.org/multipage/web-messaging.html#has-been-shipped + bool m_has_been_shipped { false }; + + // This is TransferableObject.[[Detached]] + bool m_detached { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/MessagePort.idl b/Userland/Libraries/LibWeb/HTML/MessagePort.idl new file mode 100644 index 0000000000..ca5e4b49a4 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/MessagePort.idl @@ -0,0 +1,10 @@ +interface MessagePort : EventTarget { + + undefined postMessage(any message); + undefined start(); + undefined close(); + + attribute EventHandler onmessage; + attribute EventHandler onmessageerror; + +}; |