mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-25 05:55:13 +00:00
We must reply to requests received from the client in the order they are received. The wrench in this requirement is handling requests that must be performed asynchronously, such as fetching the serialized DOM tree from the WebContent process. We currently handle this with a "block token". Async request handlers hold a token that blocks any subsequent responses from being sent. When that token is removed (i.e. the async request now has a response to be sent), the async response is then sent followed by the blocked responses in-order. This strategy had a limitation that we could not handle an actor trying to take 2 block tokens, meaning only one async request could be handled at a time. This has been fine so far, but an upcoming feature (style sheet sources) will break this limitation. The client will request N sources at a time, which would try to take N block tokens. The new strategy is to assign all requests an ID, and store a list of request IDs that are awaiting a response. When the server wants to send a reply, we match the ID of the replied-to message to this list of IDs. If it is not the first in this list, then we are blocked waiting for an earlier reply, and just store the response. When the earlier request(s) receive their response, we can then send out all blocked replies (up to the next request that has not yet received a response).
120 lines
3.6 KiB
C++
120 lines
3.6 KiB
C++
/*
|
|
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/Debug.h>
|
|
#include <AK/JsonObject.h>
|
|
#include <AK/Optional.h>
|
|
#include <AK/RefCounted.h>
|
|
#include <AK/String.h>
|
|
#include <AK/StringView.h>
|
|
#include <AK/Vector.h>
|
|
#include <AK/WeakPtr.h>
|
|
#include <AK/Weakable.h>
|
|
#include <LibDevTools/Forward.h>
|
|
|
|
namespace DevTools {
|
|
|
|
class Actor
|
|
: public RefCounted<Actor>
|
|
, public Weakable<Actor> {
|
|
public:
|
|
struct Message {
|
|
StringView type {};
|
|
JsonObject data {};
|
|
u64 id { 0 };
|
|
};
|
|
|
|
virtual ~Actor();
|
|
|
|
String const& name() const { return m_name; }
|
|
|
|
void message_received(StringView type, JsonObject);
|
|
|
|
// Use send_response when replying directly to a request received from the client.
|
|
void send_response(Message const&, JsonObject);
|
|
|
|
// Use send_message when sending an unprompted message to the client.
|
|
void send_message(JsonObject);
|
|
|
|
void send_missing_parameter_error(Optional<Message const&>, StringView parameter);
|
|
void send_unrecognized_packet_type_error(Message const&);
|
|
void send_unknown_actor_error(Optional<Message const&>, StringView actor);
|
|
|
|
protected:
|
|
explicit Actor(DevToolsServer&, String name);
|
|
|
|
virtual void handle_message(Message const&) = 0;
|
|
|
|
DevToolsServer& devtools() { return m_devtools; }
|
|
DevToolsServer const& devtools() const { return m_devtools; }
|
|
|
|
template<typename ParameterType>
|
|
auto get_required_parameter(Message const& message, StringView parameter)
|
|
{
|
|
auto result = [&]() {
|
|
if constexpr (IsIntegral<ParameterType>)
|
|
return message.data.get_integer<ParameterType>(parameter);
|
|
else if constexpr (IsSame<ParameterType, bool>)
|
|
return message.data.get_bool(parameter);
|
|
else if constexpr (IsSame<ParameterType, String>)
|
|
return message.data.get_string(parameter);
|
|
else if constexpr (IsSame<ParameterType, JsonObject>)
|
|
return message.data.get_object(parameter);
|
|
else if constexpr (IsSame<ParameterType, JsonArray>)
|
|
return message.data.get_array(parameter);
|
|
else
|
|
static_assert(DependentFalse<ParameterType>);
|
|
}();
|
|
|
|
if (!result.has_value())
|
|
send_missing_parameter_error(message, parameter);
|
|
|
|
return result;
|
|
}
|
|
|
|
template<typename ActorType = Actor, typename Handler>
|
|
auto async_handler(Optional<Message const&> message, Handler&& handler)
|
|
{
|
|
auto message_id = message.map([](auto const& message) { return message.id; });
|
|
|
|
return [weak_self = make_weak_ptr<ActorType>(), message_id, handler = forward<Handler>(handler)](auto result) mutable {
|
|
if (result.is_error()) {
|
|
dbgln_if(DEVTOOLS_DEBUG, "Error performing async action: {}", result.error());
|
|
return;
|
|
}
|
|
|
|
if (auto self = weak_self.strong_ref()) {
|
|
JsonObject response;
|
|
handler(*self, result.release_value(), response);
|
|
|
|
if (message_id.has_value())
|
|
self->send_response({ .id = *message_id }, move(response));
|
|
else
|
|
self->send_message(move(response));
|
|
}
|
|
};
|
|
}
|
|
|
|
auto default_async_handler(Message const& message)
|
|
{
|
|
return async_handler(message, [](auto&, auto, auto) { });
|
|
}
|
|
|
|
private:
|
|
DevToolsServer& m_devtools;
|
|
String m_name;
|
|
|
|
struct PendingResponse {
|
|
Optional<u64> id;
|
|
Optional<JsonObject> response;
|
|
};
|
|
Vector<PendingResponse, 1> m_pending_responses;
|
|
u64 m_next_message_id { 0 };
|
|
};
|
|
|
|
}
|