#pragma once

#include <LibHTTP/Cookie/Cookie.h>
#include <LibIPC/TransportHandle.h>
#include <LibURL/URL.h>
#include <LibWeb/Bindings/AgentType.h>
#include <LibWeb/HTML/BroadcastChannelMessage.h>
#include <AK/Error.h>
#include <AK/MemoryStream.h>
#include <AK/OwnPtr.h>
#include <AK/Platform.h>
#include <AK/Result.h>
#include <AK/Utf8View.h>
#include <LibIPC/Attachment.h>
#include <LibIPC/Connection.h>
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#include <LibIPC/File.h>
#include <LibIPC/Message.h>
#include <LibIPC/Stub.h>

#if defined(AK_COMPILER_CLANG)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdefaulted-function-deleted"
#endif

namespace Messages::WebWorkerClient {

enum class MessageID : i32 {
    DidCloseWorker = 1,
    DidFinishLoadingWorkerScript = 2,
    DidFailLoadingWorkerScript = 3,
    DidReportWorkerException = 4,
    DidRequestCookie = 5,
    DidRequestCookieResponse = 6,
    DidPostBroadcastChannelMessage = 7,
    RequestWorkerAgent = 8,
    RequestWorkerAgentResponse = 9,
};

class DidCloseWorker final : public IPC::Message {
public:
    DidCloseWorker() = default;

    DidCloseWorker(DidCloseWorker const&) = default;
    DidCloseWorker(DidCloseWorker&&) = default;
    DidCloseWorker& operator=(DidCloseWorker const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1974781266;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::DidCloseWorker; }
    static i32 static_message_id() { return (int)MessageID::DidCloseWorker; }
    virtual StringView message_name() const override { return "WebWorkerClient::DidCloseWorker"sv; }

    static ErrorOr<NonnullOwnPtr<DidCloseWorker>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        return make<DidCloseWorker>();
    }

    static ErrorOr<IPC::MessageBuffer> static_encode()
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::DidCloseWorker));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode();
    }

private:
};

class DidFinishLoadingWorkerScript final : public IPC::Message {
public:
    DidFinishLoadingWorkerScript() = default;

    DidFinishLoadingWorkerScript(DidFinishLoadingWorkerScript const&) = default;
    DidFinishLoadingWorkerScript(DidFinishLoadingWorkerScript&&) = default;
    DidFinishLoadingWorkerScript& operator=(DidFinishLoadingWorkerScript const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1974781266;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::DidFinishLoadingWorkerScript; }
    static i32 static_message_id() { return (int)MessageID::DidFinishLoadingWorkerScript; }
    virtual StringView message_name() const override { return "WebWorkerClient::DidFinishLoadingWorkerScript"sv; }

    static ErrorOr<NonnullOwnPtr<DidFinishLoadingWorkerScript>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        return make<DidFinishLoadingWorkerScript>();
    }

    static ErrorOr<IPC::MessageBuffer> static_encode()
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::DidFinishLoadingWorkerScript));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode();
    }

private:
};

class DidFailLoadingWorkerScript final : public IPC::Message {
public:
    DidFailLoadingWorkerScript() = default;

    DidFailLoadingWorkerScript(DidFailLoadingWorkerScript const&) = default;
    DidFailLoadingWorkerScript(DidFailLoadingWorkerScript&&) = default;
    DidFailLoadingWorkerScript& operator=(DidFailLoadingWorkerScript const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1974781266;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::DidFailLoadingWorkerScript; }
    static i32 static_message_id() { return (int)MessageID::DidFailLoadingWorkerScript; }
    virtual StringView message_name() const override { return "WebWorkerClient::DidFailLoadingWorkerScript"sv; }

    static ErrorOr<NonnullOwnPtr<DidFailLoadingWorkerScript>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        return make<DidFailLoadingWorkerScript>();
    }

    static ErrorOr<IPC::MessageBuffer> static_encode()
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::DidFailLoadingWorkerScript));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode();
    }

private:
};

class DidReportWorkerException final : public IPC::Message {
public:
    DidReportWorkerException(String message, String filename, u32 lineno, u32 colno)
        : m_message(move(message))
        , m_filename(move(filename))
        , m_lineno(move(lineno))
        , m_colno(move(colno))
    {
    }

    DidReportWorkerException(DidReportWorkerException const&) = default;
    DidReportWorkerException(DidReportWorkerException&&) = default;
    DidReportWorkerException& operator=(DidReportWorkerException const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1974781266;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::DidReportWorkerException; }
    static i32 static_message_id() { return (int)MessageID::DidReportWorkerException; }
    virtual StringView message_name() const override { return "WebWorkerClient::DidReportWorkerException"sv; }

    static ErrorOr<NonnullOwnPtr<DidReportWorkerException>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto message = TRY((decoder.decode<String>()));
        auto filename = TRY((decoder.decode<String>()));
        auto lineno = TRY((decoder.decode<u32>()));
        auto colno = TRY((decoder.decode<u32>()));
        return make<DidReportWorkerException>(move(message), move(filename), move(lineno), move(colno));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(StringView message, StringView filename, u32 lineno, u32 colno)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::DidReportWorkerException));
        TRY(stream.encode(message));
        TRY(stream.encode(filename));
        TRY(stream.encode(lineno));
        TRY(stream.encode(colno));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_message, m_filename, m_lineno, m_colno);
    }

    String const& message() const { return m_message; }
    String take_message() { return move(m_message); }

    String const& filename() const { return m_filename; }
    String take_filename() { return move(m_filename); }

    u32 lineno() const { return m_lineno; }

    u32 colno() const { return m_colno; }

private:
    String m_message;
    String m_filename;
    u32 m_lineno;
    u32 m_colno;
};

class DidRequestCookieResponse final : public IPC::Message {
public:
    DidRequestCookieResponse(HTTP::Cookie::VersionedCookie cookie)
        : m_cookie(move(cookie))
    {
    }

    DidRequestCookieResponse(DidRequestCookieResponse const&) = default;
    DidRequestCookieResponse(DidRequestCookieResponse&&) = default;
    DidRequestCookieResponse& operator=(DidRequestCookieResponse const&) = default;

    template<typename WrappedReturnType>
    requires(!SameAs<WrappedReturnType, HTTP::Cookie::VersionedCookie>)
    DidRequestCookieResponse(WrappedReturnType&& value)
        : m_cookie(forward<WrappedReturnType>(value))
    {
    }

    static constexpr u32 ENDPOINT_MAGIC = 1974781266;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::DidRequestCookieResponse; }
    static i32 static_message_id() { return (int)MessageID::DidRequestCookieResponse; }
    virtual StringView message_name() const override { return "WebWorkerClient::DidRequestCookieResponse"sv; }

    static ErrorOr<NonnullOwnPtr<DidRequestCookieResponse>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto cookie = TRY((decoder.decode<HTTP::Cookie::VersionedCookie>()));
        return make<DidRequestCookieResponse>(move(cookie));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(HTTP::Cookie::VersionedCookie const& cookie)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::DidRequestCookieResponse));
        TRY(stream.encode(cookie));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_cookie);
    }

    HTTP::Cookie::VersionedCookie const& cookie() const { return m_cookie; }
    HTTP::Cookie::VersionedCookie take_cookie() { return move(m_cookie); }

private:
    HTTP::Cookie::VersionedCookie m_cookie;
};

class DidRequestCookie final : public IPC::Message {
public:
    typedef class DidRequestCookieResponse ResponseType;

    DidRequestCookie(URL::URL url, HTTP::Cookie::Source source)
        : m_url(move(url))
        , m_source(move(source))
    {
    }

    DidRequestCookie(DidRequestCookie const&) = default;
    DidRequestCookie(DidRequestCookie&&) = default;
    DidRequestCookie& operator=(DidRequestCookie const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1974781266;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::DidRequestCookie; }
    static i32 static_message_id() { return (int)MessageID::DidRequestCookie; }
    virtual StringView message_name() const override { return "WebWorkerClient::DidRequestCookie"sv; }

    static ErrorOr<NonnullOwnPtr<DidRequestCookie>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto url = TRY((decoder.decode<URL::URL>()));
        auto source = TRY((decoder.decode<HTTP::Cookie::Source>()));
        return make<DidRequestCookie>(move(url), move(source));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(URL::URL const& url, HTTP::Cookie::Source source)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::DidRequestCookie));
        TRY(stream.encode(url));
        TRY(stream.encode(source));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_url, m_source);
    }

    URL::URL const& url() const { return m_url; }
    URL::URL take_url() { return move(m_url); }

    HTTP::Cookie::Source source() const { return m_source; }

private:
    URL::URL m_url;
    HTTP::Cookie::Source m_source;
};

class DidPostBroadcastChannelMessage final : public IPC::Message {
public:
    DidPostBroadcastChannelMessage(Web::HTML::BroadcastChannelMessage message)
        : m_message(move(message))
    {
    }

    DidPostBroadcastChannelMessage(DidPostBroadcastChannelMessage const&) = default;
    DidPostBroadcastChannelMessage(DidPostBroadcastChannelMessage&&) = default;
    DidPostBroadcastChannelMessage& operator=(DidPostBroadcastChannelMessage const&) = default;

    template<typename WrappedReturnType>
    requires(!SameAs<WrappedReturnType, Web::HTML::BroadcastChannelMessage>)
    DidPostBroadcastChannelMessage(WrappedReturnType&& value)
        : m_message(forward<WrappedReturnType>(value))
    {
    }

    static constexpr u32 ENDPOINT_MAGIC = 1974781266;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::DidPostBroadcastChannelMessage; }
    static i32 static_message_id() { return (int)MessageID::DidPostBroadcastChannelMessage; }
    virtual StringView message_name() const override { return "WebWorkerClient::DidPostBroadcastChannelMessage"sv; }

    static ErrorOr<NonnullOwnPtr<DidPostBroadcastChannelMessage>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto message = TRY((decoder.decode<Web::HTML::BroadcastChannelMessage>()));
        return make<DidPostBroadcastChannelMessage>(move(message));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(Web::HTML::BroadcastChannelMessage const& message)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::DidPostBroadcastChannelMessage));
        TRY(stream.encode(message));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_message);
    }

    Web::HTML::BroadcastChannelMessage const& message() const { return m_message; }
    Web::HTML::BroadcastChannelMessage take_message() { return move(m_message); }

private:
    Web::HTML::BroadcastChannelMessage m_message;
};

class RequestWorkerAgentResponse final : public IPC::Message {
public:
    RequestWorkerAgentResponse(IPC::TransportHandle handle, IPC::TransportHandle request_server_handle, IPC::TransportHandle image_decoder_handle)
        : m_handle(move(handle))
        , m_request_server_handle(move(request_server_handle))
        , m_image_decoder_handle(move(image_decoder_handle))
    {
    }

    RequestWorkerAgentResponse(RequestWorkerAgentResponse const&) = default;
    RequestWorkerAgentResponse(RequestWorkerAgentResponse&&) = default;
    RequestWorkerAgentResponse& operator=(RequestWorkerAgentResponse const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1974781266;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::RequestWorkerAgentResponse; }
    static i32 static_message_id() { return (int)MessageID::RequestWorkerAgentResponse; }
    virtual StringView message_name() const override { return "WebWorkerClient::RequestWorkerAgentResponse"sv; }

    static ErrorOr<NonnullOwnPtr<RequestWorkerAgentResponse>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto handle = TRY((decoder.decode<IPC::TransportHandle>()));
        auto request_server_handle = TRY((decoder.decode<IPC::TransportHandle>()));
        auto image_decoder_handle = TRY((decoder.decode<IPC::TransportHandle>()));
        return make<RequestWorkerAgentResponse>(move(handle), move(request_server_handle), move(image_decoder_handle));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(IPC::TransportHandle const& handle, IPC::TransportHandle const& request_server_handle, IPC::TransportHandle const& image_decoder_handle)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::RequestWorkerAgentResponse));
        TRY(stream.encode(handle));
        TRY(stream.encode(request_server_handle));
        TRY(stream.encode(image_decoder_handle));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_handle, m_request_server_handle, m_image_decoder_handle);
    }

    IPC::TransportHandle const& handle() const { return m_handle; }
    IPC::TransportHandle take_handle() { return move(m_handle); }

    IPC::TransportHandle const& request_server_handle() const { return m_request_server_handle; }
    IPC::TransportHandle take_request_server_handle() { return move(m_request_server_handle); }

    IPC::TransportHandle const& image_decoder_handle() const { return m_image_decoder_handle; }
    IPC::TransportHandle take_image_decoder_handle() { return move(m_image_decoder_handle); }

private:
    IPC::TransportHandle m_handle;
    IPC::TransportHandle m_request_server_handle;
    IPC::TransportHandle m_image_decoder_handle;
};

class RequestWorkerAgent final : public IPC::Message {
public:
    typedef class RequestWorkerAgentResponse ResponseType;

    RequestWorkerAgent(Web::Bindings::AgentType worker_type)
        : m_worker_type(move(worker_type))
    {
    }

    RequestWorkerAgent(RequestWorkerAgent const&) = default;
    RequestWorkerAgent(RequestWorkerAgent&&) = default;
    RequestWorkerAgent& operator=(RequestWorkerAgent const&) = default;

    template<typename WrappedReturnType>
    requires(!SameAs<WrappedReturnType, Web::Bindings::AgentType>)
    RequestWorkerAgent(WrappedReturnType&& value)
        : m_worker_type(forward<WrappedReturnType>(value))
    {
    }

    static constexpr u32 ENDPOINT_MAGIC = 1974781266;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::RequestWorkerAgent; }
    static i32 static_message_id() { return (int)MessageID::RequestWorkerAgent; }
    virtual StringView message_name() const override { return "WebWorkerClient::RequestWorkerAgent"sv; }

    static ErrorOr<NonnullOwnPtr<RequestWorkerAgent>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto worker_type = TRY((decoder.decode<Web::Bindings::AgentType>()));
        return make<RequestWorkerAgent>(move(worker_type));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(Web::Bindings::AgentType const& worker_type)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::RequestWorkerAgent));
        TRY(stream.encode(worker_type));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_worker_type);
    }

    Web::Bindings::AgentType const& worker_type() const { return m_worker_type; }
    Web::Bindings::AgentType take_worker_type() { return move(m_worker_type); }

private:
    Web::Bindings::AgentType m_worker_type;
};

} // namespace Messages::WebWorkerClient

template<typename LocalEndpoint, typename PeerEndpoint>
class WebWorkerClientProxy {
public:
    // Used to disambiguate the constructor call.
    struct Tag { };

    WebWorkerClientProxy(IPC::Connection<LocalEndpoint, PeerEndpoint>& connection, Tag)
        : m_connection(connection)
    {
    }

    void async_did_close_worker()
    {
        auto message_buffer = MUST(Messages::WebWorkerClient::DidCloseWorker::static_encode());
        (void)m_connection.post_message(message_buffer);
    }

    void async_did_finish_loading_worker_script()
    {
        auto message_buffer = MUST(Messages::WebWorkerClient::DidFinishLoadingWorkerScript::static_encode());
        (void)m_connection.post_message(message_buffer);
    }

    void async_did_fail_loading_worker_script()
    {
        auto message_buffer = MUST(Messages::WebWorkerClient::DidFailLoadingWorkerScript::static_encode());
        (void)m_connection.post_message(message_buffer);
    }

    void async_did_report_worker_exception(StringView message, StringView filename, u32 lineno, u32 colno)
    {
        VERIFY(Utf8View { message }.validate());
        VERIFY(Utf8View { filename }.validate());
        auto message_buffer = MUST(Messages::WebWorkerClient::DidReportWorkerException::static_encode(message, filename, lineno, colno));
        (void)m_connection.post_message(message_buffer);
    }

    void async_did_report_worker_exception(String const& message, String const& filename, u32 lineno, u32 colno)
    {
        auto message_buffer = MUST(Messages::WebWorkerClient::DidReportWorkerException::static_encode(message, filename, lineno, colno));
        (void)m_connection.post_message(message_buffer);
    }

    HTTP::Cookie::VersionedCookie did_request_cookie(URL::URL url, HTTP::Cookie::Source source)
    {
        return m_connection.template send_sync<Messages::WebWorkerClient::DidRequestCookie>(move(url), source)->take_cookie();
    }

    void async_did_request_cookie(URL::URL const& url, HTTP::Cookie::Source source)
    {
        auto message_buffer = MUST(Messages::WebWorkerClient::DidRequestCookie::static_encode(move(url), source));
        (void)m_connection.post_message(message_buffer);
    }

    IPC::IPCErrorOr<HTTP::Cookie::VersionedCookie> try_did_request_cookie(URL::URL url, HTTP::Cookie::Source source)
    {
        if (auto result = m_connection.template send_sync_but_allow_failure<Messages::WebWorkerClient::DidRequestCookie>(move(url), source))
            return move(*result);
        m_connection.shutdown();
        return IPC::ErrorCode::PeerDisconnected;
    }

    void async_did_post_broadcast_channel_message(Web::HTML::BroadcastChannelMessage const& message)
    {
        auto message_buffer = MUST(Messages::WebWorkerClient::DidPostBroadcastChannelMessage::static_encode(move(message)));
        (void)m_connection.post_message(message_buffer);
    }

    Messages::WebWorkerClient::RequestWorkerAgentResponse request_worker_agent(Web::Bindings::AgentType worker_type)
    {
        return move(*m_connection.template send_sync<Messages::WebWorkerClient::RequestWorkerAgent>(move(worker_type)));
    }

    void async_request_worker_agent(Web::Bindings::AgentType const& worker_type)
    {
        auto message_buffer = MUST(Messages::WebWorkerClient::RequestWorkerAgent::static_encode(move(worker_type)));
        (void)m_connection.post_message(message_buffer);
    }

    IPC::IPCErrorOr<Messages::WebWorkerClient::RequestWorkerAgentResponse> try_request_worker_agent(Web::Bindings::AgentType worker_type)
    {
        if (auto result = m_connection.template send_sync_but_allow_failure<Messages::WebWorkerClient::RequestWorkerAgent>(move(worker_type)))
            return move(*result);
        m_connection.shutdown();
        return IPC::ErrorCode::PeerDisconnected;
    }

private:
    IPC::Connection<LocalEndpoint, PeerEndpoint>& m_connection;
};

template<typename LocalEndpoint, typename PeerEndpoint>
class WebWorkerClientProxy;
class WebWorkerClientStub;

class WebWorkerClientEndpoint {
public:
    template<typename LocalEndpoint>
    using Proxy = WebWorkerClientProxy<LocalEndpoint, WebWorkerClientEndpoint>;
    using Stub = WebWorkerClientStub;

    static u32 static_magic() { return 1974781266; }

    static ErrorOr<NonnullOwnPtr<IPC::Message>> decode_message(ReadonlyBytes buffer, [[maybe_unused]] Queue<IPC::Attachment>& attachments)
    {
        FixedMemoryStream stream { buffer };
        auto message_endpoint_magic = TRY(stream.read_value<u32>());

        if (message_endpoint_magic != static_magic())
            return Error::from_string_literal("Endpoint magic number mismatch, not my message!");

        auto message_id = TRY(stream.read_value<i32>());

        switch (message_id) {
        case (int)Messages::WebWorkerClient::MessageID::DidCloseWorker:
            return Messages::WebWorkerClient::DidCloseWorker::decode(stream, attachments);
        case (int)Messages::WebWorkerClient::MessageID::DidFinishLoadingWorkerScript:
            return Messages::WebWorkerClient::DidFinishLoadingWorkerScript::decode(stream, attachments);
        case (int)Messages::WebWorkerClient::MessageID::DidFailLoadingWorkerScript:
            return Messages::WebWorkerClient::DidFailLoadingWorkerScript::decode(stream, attachments);
        case (int)Messages::WebWorkerClient::MessageID::DidReportWorkerException:
            return Messages::WebWorkerClient::DidReportWorkerException::decode(stream, attachments);
        case (int)Messages::WebWorkerClient::MessageID::DidRequestCookie:
            return Messages::WebWorkerClient::DidRequestCookie::decode(stream, attachments);
        case (int)Messages::WebWorkerClient::MessageID::DidRequestCookieResponse:
            return Messages::WebWorkerClient::DidRequestCookieResponse::decode(stream, attachments);
        case (int)Messages::WebWorkerClient::MessageID::DidPostBroadcastChannelMessage:
            return Messages::WebWorkerClient::DidPostBroadcastChannelMessage::decode(stream, attachments);
        case (int)Messages::WebWorkerClient::MessageID::RequestWorkerAgent:
            return Messages::WebWorkerClient::RequestWorkerAgent::decode(stream, attachments);
        case (int)Messages::WebWorkerClient::MessageID::RequestWorkerAgentResponse:
            return Messages::WebWorkerClient::RequestWorkerAgentResponse::decode(stream, attachments);
        default:
            return Error::from_string_literal("Failed to decode WebWorkerClient message");
        }

        VERIFY_NOT_REACHED();
    }
};

class WebWorkerClientStub : public IPC::Stub {
public:
    WebWorkerClientStub() { }
    virtual ~WebWorkerClientStub() override { }

    virtual u32 magic() const override { return 1974781266; }
    virtual ByteString name() const override { return "WebWorkerClient"; }

    virtual ErrorOr<OwnPtr<IPC::MessageBuffer>> handle(NonnullOwnPtr<IPC::Message> message) override
    {
        switch (message->message_id()) {
        case (int)Messages::WebWorkerClient::MessageID::DidCloseWorker:
            return handle_did_close_worker();
        case (int)Messages::WebWorkerClient::MessageID::DidFinishLoadingWorkerScript:
            return handle_did_finish_loading_worker_script();
        case (int)Messages::WebWorkerClient::MessageID::DidFailLoadingWorkerScript:
            return handle_did_fail_loading_worker_script();
        case (int)Messages::WebWorkerClient::MessageID::DidReportWorkerException:
            return handle_did_report_worker_exception(*message);
        case (int)Messages::WebWorkerClient::MessageID::DidRequestCookie:
            return handle_did_request_cookie(*message);
        case (int)Messages::WebWorkerClient::MessageID::DidPostBroadcastChannelMessage:
            return handle_did_post_broadcast_channel_message(*message);
        case (int)Messages::WebWorkerClient::MessageID::RequestWorkerAgent:
            return handle_request_worker_agent(*message);
        default:
            return Error::from_string_literal("Unknown message ID for WebWorkerClient endpoint");
        }
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_did_close_worker()
    {
        did_close_worker();
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_did_finish_loading_worker_script()
    {
        did_finish_loading_worker_script();
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_did_fail_loading_worker_script()
    {
        did_fail_loading_worker_script();
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_did_report_worker_exception(IPC::Message& message)
    {
        auto& request = static_cast<Messages::WebWorkerClient::DidReportWorkerException&>(message);
        did_report_worker_exception(request.take_message(), request.take_filename(), request.lineno(), request.colno());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_did_request_cookie(IPC::Message& message)
    {
        auto& request = static_cast<Messages::WebWorkerClient::DidRequestCookie&>(message);
        auto response = did_request_cookie(request.take_url(), request.source());
        return make<IPC::MessageBuffer>(TRY(response.encode()));
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_did_post_broadcast_channel_message(IPC::Message& message)
    {
        auto& request = static_cast<Messages::WebWorkerClient::DidPostBroadcastChannelMessage&>(message);
        did_post_broadcast_channel_message(request.take_message());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_request_worker_agent(IPC::Message& message)
    {
        auto& request = static_cast<Messages::WebWorkerClient::RequestWorkerAgent&>(message);
        auto response = request_worker_agent(request.take_worker_type());
        return make<IPC::MessageBuffer>(TRY(response.encode()));
    }

    virtual void did_close_worker() = 0;
    virtual void did_finish_loading_worker_script() = 0;
    virtual void did_fail_loading_worker_script() = 0;
    virtual void did_report_worker_exception(String message, String filename, u32 lineno, u32 colno) = 0;
    virtual Messages::WebWorkerClient::DidRequestCookieResponse did_request_cookie(URL::URL url, HTTP::Cookie::Source source) = 0;
    virtual void did_post_broadcast_channel_message(Web::HTML::BroadcastChannelMessage message) = 0;
    virtual Messages::WebWorkerClient::RequestWorkerAgentResponse request_worker_agent(Web::Bindings::AgentType worker_type) = 0;
};

#if defined(AK_COMPILER_CLANG)
#pragma clang diagnostic pop
#endif
