mirror of
https://github.com/zebrajr/ladybird.git
synced 2025-12-06 00:19:53 +01: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).
163 lines
6.0 KiB
C++
163 lines
6.0 KiB
C++
/*
|
|
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/JsonObject.h>
|
|
#include <AK/JsonValue.h>
|
|
#include <LibDevTools/Actors/InspectorActor.h>
|
|
#include <LibDevTools/Actors/PageStyleActor.h>
|
|
#include <LibDevTools/Actors/TabActor.h>
|
|
#include <LibDevTools/Actors/WalkerActor.h>
|
|
#include <LibDevTools/DevToolsDelegate.h>
|
|
#include <LibDevTools/DevToolsServer.h>
|
|
|
|
namespace DevTools {
|
|
|
|
static void received_layout(JsonObject& response, JsonObject const& computed_style, JsonObject const& node_box_sizing)
|
|
{
|
|
response.set("autoMargins"sv, JsonObject {});
|
|
|
|
auto pixel_value = [&](auto const& object, auto key) {
|
|
return object.get_double_with_precision_loss(key).value_or(0);
|
|
};
|
|
auto set_pixel_value_from = [&](auto const& object, auto object_key, auto message_key) {
|
|
response.set(message_key, MUST(String::formatted("{}px", pixel_value(object, object_key))));
|
|
};
|
|
auto set_computed_value_from = [&](auto const& object, auto key) {
|
|
response.set(key, object.get_string(key).value_or(String {}));
|
|
};
|
|
|
|
response.set("width"sv, pixel_value(node_box_sizing, "content_width"sv));
|
|
response.set("height"sv, pixel_value(node_box_sizing, "content_height"sv));
|
|
|
|
// FIXME: This response should also contain "top", "right", "bottom", and "left", but our box model metrics in
|
|
// WebContent do not provide this information.
|
|
|
|
set_pixel_value_from(node_box_sizing, "border_top"sv, "border-top-width"sv);
|
|
set_pixel_value_from(node_box_sizing, "border_right"sv, "border-right-width"sv);
|
|
set_pixel_value_from(node_box_sizing, "border_bottom"sv, "border-bottom-width"sv);
|
|
set_pixel_value_from(node_box_sizing, "border_left"sv, "border-left-width"sv);
|
|
|
|
set_pixel_value_from(node_box_sizing, "margin_top"sv, "margin-top"sv);
|
|
set_pixel_value_from(node_box_sizing, "margin_right"sv, "margin-right"sv);
|
|
set_pixel_value_from(node_box_sizing, "margin_bottom"sv, "margin-bottom"sv);
|
|
set_pixel_value_from(node_box_sizing, "margin_left"sv, "margin-left"sv);
|
|
|
|
set_pixel_value_from(node_box_sizing, "padding_top"sv, "padding-top"sv);
|
|
set_pixel_value_from(node_box_sizing, "padding_right"sv, "padding-right"sv);
|
|
set_pixel_value_from(node_box_sizing, "padding_bottom"sv, "padding-bottom"sv);
|
|
set_pixel_value_from(node_box_sizing, "padding_left"sv, "padding-left"sv);
|
|
|
|
set_computed_value_from(computed_style, "box-sizing"sv);
|
|
set_computed_value_from(computed_style, "display"sv);
|
|
set_computed_value_from(computed_style, "float"sv);
|
|
set_computed_value_from(computed_style, "line-height"sv);
|
|
set_computed_value_from(computed_style, "position"sv);
|
|
set_computed_value_from(computed_style, "z-index"sv);
|
|
}
|
|
|
|
static void received_computed_style(JsonObject& response, JsonObject const& computed_style)
|
|
{
|
|
JsonObject computed;
|
|
|
|
computed_style.for_each_member([&](String const& name, JsonValue const& value) {
|
|
JsonObject property;
|
|
property.set("matched"sv, true);
|
|
property.set("value"sv, value);
|
|
computed.set(name, move(property));
|
|
});
|
|
|
|
response.set("computed"sv, move(computed));
|
|
}
|
|
|
|
NonnullRefPtr<PageStyleActor> PageStyleActor::create(DevToolsServer& devtools, String name, WeakPtr<InspectorActor> inspector)
|
|
{
|
|
return adopt_ref(*new PageStyleActor(devtools, move(name), move(inspector)));
|
|
}
|
|
|
|
PageStyleActor::PageStyleActor(DevToolsServer& devtools, String name, WeakPtr<InspectorActor> inspector)
|
|
: Actor(devtools, move(name))
|
|
, m_inspector(move(inspector))
|
|
{
|
|
}
|
|
|
|
PageStyleActor::~PageStyleActor() = default;
|
|
|
|
void PageStyleActor::handle_message(Message const& message)
|
|
{
|
|
JsonObject response;
|
|
|
|
if (message.type == "getApplied"sv) {
|
|
// FIXME: This provides information to the "styles" pane in the inspector tab, which allows toggling and editing
|
|
// styles live. We do not yet support figuring out the list of styles that apply to a specific node.
|
|
response.set("entries"sv, JsonArray {});
|
|
send_response(message, move(response));
|
|
return;
|
|
}
|
|
|
|
if (message.type == "getComputed"sv) {
|
|
auto node = get_required_parameter<String>(message, "node"sv);
|
|
if (!node.has_value())
|
|
return;
|
|
|
|
inspect_dom_node(message, *node, [](auto& response, auto const& properties) {
|
|
received_computed_style(response, properties.computed_style);
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
if (message.type == "getLayout"sv) {
|
|
auto node = get_required_parameter<String>(message, "node"sv);
|
|
if (!node.has_value())
|
|
return;
|
|
|
|
inspect_dom_node(message, *node, [](auto& response, auto const& properties) {
|
|
received_layout(response, properties.computed_style, properties.node_box_sizing);
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
if (message.type == "isPositionEditable") {
|
|
response.set("value"sv, false);
|
|
send_response(message, move(response));
|
|
return;
|
|
}
|
|
|
|
send_unrecognized_packet_type_error(message);
|
|
}
|
|
|
|
JsonValue PageStyleActor::serialize_style() const
|
|
{
|
|
JsonObject traits;
|
|
traits.set("fontStyleLevel4"sv, true);
|
|
traits.set("fontWeightLevel4"sv, true);
|
|
traits.set("fontStretchLevel4"sv, true);
|
|
traits.set("fontVariations"sv, true);
|
|
|
|
JsonObject style;
|
|
style.set("actor"sv, name());
|
|
style.set("traits"sv, move(traits));
|
|
return style;
|
|
}
|
|
|
|
template<typename Callback>
|
|
void PageStyleActor::inspect_dom_node(Message const& message, StringView node_actor, Callback&& callback)
|
|
{
|
|
auto dom_node = WalkerActor::dom_node_for(InspectorActor::walker_for(m_inspector), node_actor);
|
|
if (!dom_node.has_value()) {
|
|
send_unknown_actor_error(message, node_actor);
|
|
return;
|
|
}
|
|
|
|
devtools().delegate().inspect_dom_node(dom_node->tab->description(), dom_node->identifier.id, dom_node->identifier.pseudo_element,
|
|
async_handler(message, [callback = forward<Callback>(callback)](auto&, auto properties, auto& response) {
|
|
callback(response, properties);
|
|
}));
|
|
}
|
|
|
|
}
|