ladybird/Libraries/LibWeb/HTML/MessagePort.h
Shannon Booth 02911253dd LibWeb+LibIPC: Preserve MessagePort queue state across transfer
A MessagePort can be transferred while it already has local queued
state such as incoming messages drained from its transport,
outgoing messages posted before a transport exists, and a pending
shutdown to apply once the port is enabled.

Serialize and restore that state as part of transfer so it moves with
the port instead of being left behind on the old transport.

Also mark transports that are being transferred so shutdown of the old
endpoint during handoff is not reported as peer EOF. That shutdown is
part of moving the transport to the new owner, not peer disconnected.

Co-Authored-By: Alexander Kalenik <kalenik.aliaksandr@gmail.com>
2026-04-09 19:59:16 +02:00

107 lines
3.7 KiB
C++

/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/RefCounted.h>
#include <AK/Weakable.h>
#include <LibCore/Socket.h>
#include <LibIPC/File.h>
#include <LibIPC/Transport.h>
#include <LibWeb/Bindings/Transferable.h>
#include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/Export.h>
#include <LibWeb/Forward.h>
namespace Web::HTML {
// https://html.spec.whatwg.org/multipage/web-messaging.html#message-ports
class WEB_API MessagePort final
: public DOM::EventTarget
, public Bindings::Transferable {
WEB_PLATFORM_OBJECT(MessagePort, DOM::EventTarget);
GC_DECLARE_ALLOCATOR(MessagePort);
public:
static constexpr bool OVERRIDES_FINALIZE = true;
[[nodiscard]] static GC::Ref<MessagePort> create(JS::Realm&);
static void for_each_message_port(Function<void(MessagePort&)>);
virtual ~MessagePort() override;
// https://html.spec.whatwg.org/multipage/web-messaging.html#entangle
void entangle_with(MessagePort&);
void disentangle();
GC::Ptr<MessagePort> entangled_port() { return m_remote_port; }
GC::Ptr<MessagePort const> entangled_port() const { return m_remote_port; }
// https://html.spec.whatwg.org/multipage/web-messaging.html#dom-messageport-postmessage
WebIDL::ExceptionOr<void> post_message(JS::Value message, Vector<GC::Root<JS::Object>> const& transfer);
// https://html.spec.whatwg.org/multipage/web-messaging.html#dom-messageport-postmessage-options
WebIDL::ExceptionOr<void> post_message(JS::Value message, StructuredSerializeOptions const& options);
void enable();
void start();
void close();
void set_onmessageerror(GC::Ptr<WebIDL::CallbackType>);
GC::Ptr<WebIDL::CallbackType> onmessageerror();
void set_onmessage(GC::Ptr<WebIDL::CallbackType>);
GC::Ptr<WebIDL::CallbackType> onmessage();
// ^Transferable
virtual WebIDL::ExceptionOr<void> transfer_steps(HTML::TransferDataEncoder&) override;
virtual WebIDL::ExceptionOr<void> transfer_receiving_steps(HTML::TransferDataDecoder&) override;
virtual HTML::TransferType primary_interface() const override { return HTML::TransferType::MessagePort; }
void set_worker_event_target(GC::Ref<DOM::EventTarget>);
WebIDL::ExceptionOr<void> message_port_post_message_steps(GC::Ptr<MessagePort> target_port, JS::Value message, StructuredSerializeOptions const& options);
private:
explicit MessagePort(JS::Realm&);
virtual void initialize(JS::Realm&) override;
virtual void finalize() override;
virtual void visit_edges(Cell::Visitor&) override;
bool is_entangled() const;
void dispatch_pending_messages();
void flush_pending_outgoing_messages();
void queue_message_task(SerializedTransferRecord&&);
void drain_transport();
void post_message_task_steps(SerializedTransferRecord&);
void post_port_message(SerializedTransferRecord const&);
ErrorOr<void> send_message_on_transport(SerializedTransferRecord const&);
void read_from_transport();
// The HTML spec implies(!) that this is MessagePort.[[RemotePort]]
GC::Ptr<MessagePort> m_remote_port;
// https://html.spec.whatwg.org/multipage/web-messaging.html#has-been-shipped
bool m_has_been_shipped { false };
OwnPtr<IPC::Transport> m_transport;
GC::Ptr<DOM::EventTarget> m_worker_event_target;
Vector<SerializedTransferRecord> m_pending_incoming_messages;
Vector<SerializedTransferRecord> m_pending_outgoing_messages;
bool m_should_shutdown_on_enable { false };
bool m_enabled { false };
};
}