Sfoglia il codice sorgente

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.
Andreas Kling 3 anni fa
parent
commit
95559c4277

+ 7 - 0
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>

+ 1 - 0
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>

+ 2 - 0
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>

+ 3 - 0
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)                      \

+ 4 - 0
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)

+ 4 - 0
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;

+ 31 - 0
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()
+{
+}
+
+}

+ 47 - 0
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;
+};
+
+}

+ 8 - 0
Userland/Libraries/LibWeb/HTML/MessageChannel.idl

@@ -0,0 +1,8 @@
+interface MessageChannel {
+
+    constructor();
+
+    readonly attribute MessagePort port1;
+    readonly attribute MessagePort port2;
+
+};

+ 126 - 0
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
+
+}

+ 79 - 0
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 };
+};
+
+}

+ 10 - 0
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;
+
+};