Bläddra i källkod

LibWeb: Add a Transferable interface to model the transferable property

This property is only shared by MessagePort and a few Image related APIs
but is important for the Structured{De}SerializeWithTransfer AOs.
Andrew Kaster 1 år sedan
förälder
incheckning
e21d1078a0

+ 37 - 0
Userland/Libraries/LibWeb/Bindings/Transferable.h

@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Vector.h>
+#include <LibIPC/Forward.h>
+#include <LibWeb/HTML/StructuredSerialize.h>
+#include <LibWeb/WebIDL/ExceptionOr.h>
+
+namespace Web::Bindings {
+
+// https://html.spec.whatwg.org/multipage/structured-data.html#transferable-objects
+class Transferable {
+public:
+    virtual ~Transferable() = default;
+
+    // NOTE: It is an error to call Base::transfer_steps in your impl
+    virtual WebIDL::ExceptionOr<void> transfer_steps(HTML::TransferDataHolder&) = 0;
+
+    // NOTE: It is an error to call Base::transfer_receiving_steps in your impl
+    virtual WebIDL::ExceptionOr<void> transfer_receiving_steps(HTML::TransferDataHolder const&) = 0;
+
+    virtual HTML::TransferType primary_interface() const = 0;
+
+    bool is_detached() const { return m_detached; }
+    void set_detached(bool b) { m_detached = b; }
+
+private:
+    // https://html.spec.whatwg.org/multipage/structured-data.html#detached
+    bool m_detached = false;
+};
+
+}

+ 42 - 1
Userland/Libraries/LibWeb/HTML/MessagePort.cpp

@@ -40,6 +40,47 @@ void MessagePort::visit_edges(Cell::Visitor& visitor)
     visitor.visit(m_remote_port);
 }
 
+// https://html.spec.whatwg.org/multipage/web-messaging.html#message-ports:transfer-steps
+WebIDL::ExceptionOr<void> MessagePort::transfer_steps(HTML::TransferDataHolder&)
+{
+    // 1. Set value's has been shipped flag to true.
+    m_has_been_shipped = true;
+
+    // FIXME: 2. Set dataHolder.[[PortMessageQueue]] to value's port message queue.
+    // FIXME: Support delivery of messages that haven't been delivered yet on the other side
+
+    // 3. If value is entangled with another port remotePort, then:
+    if (is_entangled()) {
+        // 1. Set remotePort's has been shipped flag to true.
+        m_remote_port->m_has_been_shipped = true;
+
+        // 2. Set dataHolder.[[RemotePort]] to remotePort.
+        // FIXME: Append an IPC::File to the dataHolder
+    }
+
+    // 4. Otherwise, set dataHolder.[[RemotePort]] to null.
+    else {
+        // FIXME: Note in the dataHolder that there are no fds
+    }
+
+    return {};
+}
+
+WebIDL::ExceptionOr<void> MessagePort::transfer_receiving_steps(HTML::TransferDataHolder const&)
+{
+    // 1. Set value's has been shipped flag to true.
+    m_has_been_shipped = true;
+
+    // FIXME 2. Move all the tasks that are to fire message events in dataHolder.[[PortMessageQueue]] to the port message queue of value,
+    //     if any, leaving value's port message queue in its initial disabled state, and, if value's relevant global object is a Window,
+    //     associating the moved tasks with value's relevant global object's associated Document.
+
+    // FIXME: 3. If dataHolder.[[RemotePort]] is not null, then entangle dataHolder.[[RemotePort]] and value.
+    //     (This will disentangle dataHolder.[[RemotePort]] from the original port that was transferred.)
+
+    return {};
+}
+
 void MessagePort::disentangle()
 {
     m_remote_port->m_remote_port = nullptr;
@@ -112,7 +153,7 @@ void MessagePort::start()
 void MessagePort::close()
 {
     // 1. Set this MessagePort object's [[Detached]] internal slot value to true.
-    m_detached = true;
+    set_detached(true);
 
     // 2. If this MessagePort object is entangled, disentangle it.
     if (is_entangled())

+ 8 - 4
Userland/Libraries/LibWeb/HTML/MessagePort.h

@@ -8,6 +8,7 @@
 
 #include <AK/RefCounted.h>
 #include <AK/Weakable.h>
+#include <LibWeb/Bindings/Transferable.h>
 #include <LibWeb/DOM/EventTarget.h>
 #include <LibWeb/Forward.h>
 
@@ -23,7 +24,8 @@ struct StructuredSerializeOptions {
 };
 
 // https://html.spec.whatwg.org/multipage/web-messaging.html#message-ports
-class MessagePort final : public DOM::EventTarget {
+class MessagePort final : public DOM::EventTarget
+    , public Bindings::Transferable {
     WEB_PLATFORM_OBJECT(MessagePort, DOM::EventTarget);
     JS_DECLARE_ALLOCATOR(MessagePort);
 
@@ -49,6 +51,11 @@ public:
     ENUMERATE_MESSAGE_PORT_EVENT_HANDLERS(__ENUMERATE)
 #undef __ENUMERATE
 
+    // ^Transferable
+    virtual WebIDL::ExceptionOr<void> transfer_steps(HTML::TransferDataHolder&) override;
+    virtual WebIDL::ExceptionOr<void> transfer_receiving_steps(HTML::TransferDataHolder const&) override;
+    virtual HTML::TransferType primary_interface() const override { return HTML::TransferType::MessagePort; }
+
 private:
     explicit MessagePort(JS::Realm&);
 
@@ -63,9 +70,6 @@ private:
 
     // 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 };
 };
 
 }

+ 20 - 0
Userland/Libraries/LibWeb/HTML/StructuredSerialize.h

@@ -10,6 +10,7 @@
 #include <AK/Result.h>
 #include <AK/Types.h>
 #include <AK/Vector.h>
+#include <LibIPC/Forward.h>
 #include <LibJS/Forward.h>
 #include <LibWeb/WebIDL/ExceptionOr.h>
 
@@ -24,6 +25,25 @@ using SerializationRecord = Vector<u32>;
 using SerializationMemory = HashMap<JS::Handle<JS::Value>, u32>;
 using DeserializationMemory = JS::MarkedVector<JS::Value>;
 
+struct TransferDataHolder {
+    Vector<u8> data;
+    Vector<IPC::File> fds;
+};
+
+struct SerializedTransferRecord {
+    SerializationRecord serialized;
+    Vector<TransferDataHolder> transfer_data_holders;
+};
+
+struct DeserializedTransferRecord {
+    JS::Value deserialized;
+    JS::MarkedVector<JS::Value> transferred_values;
+};
+
+enum class TransferType : u8 {
+    MessagePort,
+};
+
 WebIDL::ExceptionOr<SerializationRecord> structured_serialize(JS::VM& vm, JS::Value);
 WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM& vm, JS::Value);
 WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value, bool for_storage, SerializationMemory&);