/* * Copyright (c) 2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include namespace DevTools { // FIXME: Convert `name` to a String. Actor::Actor(DevToolsServer& devtools, ByteString name) : m_devtools(devtools) , m_name(MUST(String::from_byte_string(name))) { } Actor::~Actor() = default; void Actor::send_message(JsonValue message, Optional block_token) { if (m_block_responses && !block_token.has_value()) { m_blocked_responses.append(move(message)); return; } if (auto& connection = devtools().connection()) connection->send_message(message); } // https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html#error-packets void Actor::send_missing_parameter_error(StringView parameter) { JsonObject error; error.set("from"sv, name()); error.set("error"sv, "missingParameter"sv); error.set("message"sv, MUST(String::formatted("Missing parameter: '{}'", parameter))); 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("from"sv, name()); error.set("error"sv, "unrecognizedPacketType"sv); error.set("message"sv, MUST(String::formatted("Unrecognized packet type: '{}'", type))); 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("from"sv, name()); error.set("error"sv, "unknownActor"sv); error.set("message"sv, MUST(String::formatted("Unknown actor: '{}'", actor))); 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) : 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)); } }