2025-02-15 07:35:58 -05:00
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <LibDevTools/Actor.h>
|
|
|
|
|
#include <LibDevTools/Connection.h>
|
|
|
|
|
#include <LibDevTools/DevToolsServer.h>
|
|
|
|
|
|
|
|
|
|
namespace DevTools {
|
|
|
|
|
|
2025-02-19 09:28:02 -05:00
|
|
|
|
Actor::Actor(DevToolsServer& devtools, String name)
|
2025-02-15 07:35:58 -05:00
|
|
|
|
: m_devtools(devtools)
|
2025-02-19 09:28:02 -05:00
|
|
|
|
, m_name(move(name))
|
2025-02-15 07:35:58 -05:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Actor::~Actor() = default;
|
|
|
|
|
|
2025-03-11 08:15:23 -04:00
|
|
|
|
void Actor::send_message(JsonObject message, Optional<BlockToken> block_token)
|
2025-02-15 07:35:58 -05:00
|
|
|
|
{
|
|
|
|
|
if (m_block_responses && !block_token.has_value()) {
|
|
|
|
|
m_blocked_responses.append(move(message));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-11 08:15:23 -04:00
|
|
|
|
message.set("from"sv, name());
|
|
|
|
|
|
2025-02-15 07:35:58 -05:00
|
|
|
|
if (auto& connection = devtools().connection())
|
2025-03-11 08:15:23 -04:00
|
|
|
|
connection->send_message(move(message));
|
2025-02-15 07:35:58 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#error-packets
|
|
|
|
|
void Actor::send_missing_parameter_error(StringView parameter)
|
|
|
|
|
{
|
|
|
|
|
JsonObject error;
|
|
|
|
|
error.set("error"sv, "missingParameter"sv);
|
2025-02-17 13:21:07 -05:00
|
|
|
|
error.set("message"sv, MUST(String::formatted("Missing parameter: '{}'", parameter)));
|
2025-02-15 07:35:58 -05:00
|
|
|
|
send_message(move(error));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#error-packets
|
|
|
|
|
void Actor::send_unrecognized_packet_type_error(StringView type)
|
|
|
|
|
{
|
|
|
|
|
JsonObject error;
|
|
|
|
|
error.set("error"sv, "unrecognizedPacketType"sv);
|
2025-02-17 13:21:07 -05:00
|
|
|
|
error.set("message"sv, MUST(String::formatted("Unrecognized packet type: '{}'", type)));
|
2025-02-15 07:35:58 -05:00
|
|
|
|
send_message(move(error));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://github.com/mozilla/gecko-dev/blob/master/devtools/server/actors/object.js
|
|
|
|
|
// This error is not documented, but is used by Firefox nonetheless.
|
|
|
|
|
void Actor::send_unknown_actor_error(StringView actor)
|
|
|
|
|
{
|
|
|
|
|
JsonObject error;
|
|
|
|
|
error.set("error"sv, "unknownActor"sv);
|
2025-02-17 13:21:07 -05:00
|
|
|
|
error.set("message"sv, MUST(String::formatted("Unknown actor: '{}'", actor)));
|
2025-02-15 07:35:58 -05:00
|
|
|
|
send_message(move(error));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Actor::BlockToken Actor::block_responses()
|
|
|
|
|
{
|
|
|
|
|
// https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#the-request-reply-pattern
|
|
|
|
|
// The actor processes packets in the order they are received, and the client can trust that the i’th reply
|
|
|
|
|
// corresponds to the i’th request.
|
|
|
|
|
|
|
|
|
|
// The above requirement gets tricky for actors which require an async implementation. For example, the "getWalker"
|
|
|
|
|
// message sent to the InspectorActor results in the server fetching the DOM tree as JSON from the WebContent process.
|
|
|
|
|
// We cannot reply to the message until that is received. However, we will likely receive more messages from the
|
|
|
|
|
// client in that time. We cannot reply to those messages until we've replied to the "getWalker" message. Thus, we
|
|
|
|
|
// use this token to queue responses from the actor until that reply can be sent.
|
|
|
|
|
return { {}, *this };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Actor::BlockToken::BlockToken(Badge<Actor>, Actor& actor)
|
|
|
|
|
: m_actor(actor)
|
|
|
|
|
{
|
|
|
|
|
// If we end up in a situtation where an actor has multiple async handlers at once, we will need to come up with a
|
|
|
|
|
// more sophisticated blocking mechanism.
|
|
|
|
|
VERIFY(!actor.m_block_responses);
|
|
|
|
|
actor.m_block_responses = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Actor::BlockToken::BlockToken(BlockToken&& other)
|
|
|
|
|
: m_actor(move(other.m_actor))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Actor::BlockToken& Actor::BlockToken::operator=(BlockToken&& other)
|
|
|
|
|
{
|
|
|
|
|
m_actor = move(other.m_actor);
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Actor::BlockToken::~BlockToken()
|
|
|
|
|
{
|
|
|
|
|
auto actor = m_actor.strong_ref();
|
|
|
|
|
if (!actor)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto blocked_responses = move(actor->m_blocked_responses);
|
|
|
|
|
actor->m_block_responses = false;
|
|
|
|
|
|
|
|
|
|
for (auto& message : blocked_responses)
|
|
|
|
|
actor->send_message(move(message));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|