LibDevTools: Re-implement how we handle ordered responses

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).
This commit is contained in:
Timothy Flynn 2025-03-12 12:26:58 -04:00 committed by Tim Flynn
commit f9e83af475
Notes: github-actions[bot] 2025-03-13 20:57:51 +00:00
20 changed files with 165 additions and 168 deletions

View file

@ -47,7 +47,7 @@ void CSSPropertiesActor::handle_message(Message const& message)
}
response.set("properties"sv, move(properties));
send_message(move(response));
send_response(message, move(response));
return;
}

View file

@ -46,7 +46,7 @@ void ConsoleActor::handle_message(Message const& message)
if (message.type == "autocomplete"sv) {
response.set("matches"sv, JsonArray {});
response.set("matchProp"sv, String {});
send_message(move(response));
send_response(message, move(response));
return;
}
@ -58,7 +58,7 @@ void ConsoleActor::handle_message(Message const& message)
auto result_id = MUST(String::formatted("{}-{}", name(), m_execution_id++));
response.set("resultID"sv, result_id);
send_message(move(response));
send_response(message, move(response));
// FIXME: We do not support eager evaluation of scripts. Just bail for now.
if (message.data.get_bool("eager"sv).value_or(false)) {
@ -67,7 +67,7 @@ void ConsoleActor::handle_message(Message const& message)
if (auto tab = m_tab.strong_ref()) {
devtools().delegate().evaluate_javascript(tab->description(), *text,
async_handler([result_id, input = *text](auto&, auto result, auto& response) {
async_handler({}, [result_id, input = *text](auto&, auto result, auto& response) {
received_console_result(response, move(result_id), move(input), move(result));
}));
}

View file

@ -49,7 +49,7 @@ void DeviceActor::handle_message(Message const& message)
JsonObject response;
response.set("value"sv, move(value));
send_message(move(response));
send_response(message, move(response));
return;
}

View file

@ -62,12 +62,12 @@ void FrameActor::handle_message(Message const& message)
tab->reset_selected_node();
}
send_message(move(response));
send_response(message, move(response));
return;
}
if (message.type == "listFrames"sv) {
send_message(move(response));
send_response(message, move(response));
return;
}

View file

@ -43,7 +43,7 @@ void HighlighterActor::handle_message(Message const& message)
response.set("value"sv, true);
}
send_message(move(response));
send_response(message, move(response));
return;
}
@ -51,7 +51,7 @@ void HighlighterActor::handle_message(Message const& message)
if (auto tab = InspectorActor::tab_for(m_inspector))
devtools().delegate().clear_highlighted_dom_node(tab->description());
send_message(move(response));
send_response(message, move(response));
return;
}

View file

@ -38,7 +38,7 @@ void InspectorActor::handle_message(Message const& message)
m_page_style = devtools().register_actor<PageStyleActor>(*this);
response.set("pageStyle"sv, m_page_style->serialize_style());
send_message(move(response));
send_response(message, move(response));
return;
}
@ -52,14 +52,14 @@ void InspectorActor::handle_message(Message const& message)
});
response.set("highlighter"sv, highlighter->serialize_highlighter());
send_message(move(response));
send_response(message, move(response));
return;
}
if (message.type == "getWalker"sv) {
if (auto tab = m_tab.strong_ref()) {
devtools().delegate().inspect_tab(tab->description(),
async_handler<InspectorActor>([](auto& self, auto dom_tree, auto& response) {
async_handler<InspectorActor>(message, [](auto& self, auto dom_tree, auto& response) {
if (!WalkerActor::is_suitable_for_dom_inspection(dom_tree)) {
dbgln_if(DEVTOOLS_DEBUG, "Did not receive a suitable DOM tree: {}", dom_tree);
return;
@ -74,7 +74,7 @@ void InspectorActor::handle_message(Message const& message)
if (message.type == "supportsHighlighters"sv) {
response.set("value"sv, true);
send_message(move(response));
send_response(message, move(response));
return;
}

View file

@ -28,13 +28,13 @@ void LayoutInspectorActor::handle_message(Message const& message)
if (message.type == "getCurrentFlexbox"sv) {
response.set("flexbox"sv, JsonValue {});
send_message(move(response));
send_response(message, move(response));
return;
}
if (message.type == "getGrids"sv) {
response.set("grids"sv, JsonArray {});
send_message(move(response));
send_response(message, move(response));
return;
}

View file

@ -100,12 +100,12 @@ void NodeActor::handle_message(Message const& message)
if (message.type == "getUniqueSelector"sv) {
auto dom_node = WalkerActor::dom_node_for(m_walker, name());
if (!dom_node.has_value()) {
send_unknown_actor_error(name());
send_unknown_actor_error(message, name());
return;
}
response.set("value"sv, dom_node->node.get_string("name"sv)->to_ascii_lowercase());
send_message(move(response));
send_response(message, move(response));
return;
}
@ -120,14 +120,14 @@ void NodeActor::handle_message(Message const& message)
auto dom_node = WalkerActor::dom_node_for(m_walker, name());
if (!dom_node.has_value()) {
send_unknown_actor_error(name());
send_unknown_actor_error(message, name());
return;
}
if (attribute_to_replace.has_value())
devtools().delegate().replace_dom_node_attribute(dom_node->tab->description(), dom_node->identifier.id, *attribute_to_replace, move(replacement_attributes), default_async_handler());
devtools().delegate().replace_dom_node_attribute(dom_node->tab->description(), dom_node->identifier.id, *attribute_to_replace, move(replacement_attributes), default_async_handler(message));
else
devtools().delegate().add_dom_node_attributes(dom_node->tab->description(), dom_node->identifier.id, move(replacement_attributes), default_async_handler());
devtools().delegate().add_dom_node_attributes(dom_node->tab->description(), dom_node->identifier.id, move(replacement_attributes), default_async_handler(message));
return;
}
@ -139,11 +139,11 @@ void NodeActor::handle_message(Message const& message)
auto dom_node = WalkerActor::dom_node_for(m_walker, name());
if (!dom_node.has_value()) {
send_unknown_actor_error(name());
send_unknown_actor_error(message, name());
return;
}
devtools().delegate().set_dom_node_text(dom_node->tab->description(), dom_node->identifier.id, *value, default_async_handler());
devtools().delegate().set_dom_node_text(dom_node->tab->description(), dom_node->identifier.id, *value, default_async_handler(message));
return;
}

View file

@ -93,7 +93,7 @@ void PageStyleActor::handle_message(Message const& message)
// 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_message(move(response));
send_response(message, move(response));
return;
}
@ -102,7 +102,7 @@ void PageStyleActor::handle_message(Message const& message)
if (!node.has_value())
return;
inspect_dom_node(*node, [](auto& response, auto const& properties) {
inspect_dom_node(message, *node, [](auto& response, auto const& properties) {
received_computed_style(response, properties.computed_style);
});
@ -114,7 +114,7 @@ void PageStyleActor::handle_message(Message const& message)
if (!node.has_value())
return;
inspect_dom_node(*node, [](auto& response, auto const& properties) {
inspect_dom_node(message, *node, [](auto& response, auto const& properties) {
received_layout(response, properties.computed_style, properties.node_box_sizing);
});
@ -123,7 +123,7 @@ void PageStyleActor::handle_message(Message const& message)
if (message.type == "isPositionEditable") {
response.set("value"sv, false);
send_message(move(response));
send_response(message, move(response));
return;
}
@ -145,16 +145,16 @@ JsonValue PageStyleActor::serialize_style() const
}
template<typename Callback>
void PageStyleActor::inspect_dom_node(StringView node_actor, Callback&& 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(node_actor);
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([callback = forward<Callback>(callback)](auto&, auto properties, auto& response) {
async_handler(message, [callback = forward<Callback>(callback)](auto&, auto properties, auto& response) {
callback(response, properties);
}));
}

View file

@ -32,7 +32,7 @@ private:
virtual void handle_message(Message const&) override;
template<typename Callback>
void inspect_dom_node(StringView node_actor, Callback&&);
void inspect_dom_node(Message const&, StringView node_actor, Callback&&);
WeakPtr<InspectorActor> m_inspector;
};

View file

@ -31,7 +31,7 @@ void PreferenceActor::handle_message(Message const& message)
if (message.type == "getBoolPref"sv) {
JsonObject response;
response.set("value"sv, false);
send_message(move(response));
send_response(message, move(response));
return;
}

View file

@ -46,7 +46,7 @@ void RootActor::handle_message(Message const& message)
JsonObject response;
if (message.type == "connect") {
send_message(move(response));
send_response(message, move(response));
return;
}
@ -60,7 +60,7 @@ void RootActor::handle_message(Message const& message)
response.set("preferenceActor"sv, actor.key);
}
send_message(move(response));
send_response(message, move(response));
return;
}
@ -80,7 +80,7 @@ void RootActor::handle_message(Message const& message)
break;
}
send_message(move(response));
send_response(message, move(response));
return;
}
@ -100,13 +100,13 @@ void RootActor::handle_message(Message const& message)
break;
}
send_message(move(response));
send_response(message, move(response));
return;
}
if (message.type == "listAddons"sv) {
response.set("addons"sv, JsonArray {});
send_message(move(response));
send_response(message, move(response));
return;
}
@ -119,13 +119,13 @@ void RootActor::handle_message(Message const& message)
}
response.set("processes"sv, move(processes));
send_message(move(response));
send_response(message, move(response));
return;
}
if (message.type == "listServiceWorkerRegistrations"sv) {
response.set("registrations"sv, JsonArray {});
send_message(move(response));
send_response(message, move(response));
return;
}
@ -140,13 +140,13 @@ void RootActor::handle_message(Message const& message)
}
response.set("tabs"sv, move(tabs));
send_message(move(response));
send_response(message, move(response));
return;
}
if (message.type == "listWorkers"sv) {
response.set("workers"sv, JsonArray {});
send_message(move(response));
send_response(message, move(response));
return;
}

View file

@ -36,7 +36,7 @@ void TabActor::handle_message(Message const& message)
// FIXME: Firefox DevTools wants a favicon URL here, but supplying a URL seems to prevent this tab from being
// listed on the about:debugging page. Both Servo and Firefox itself supply `null` here.
response.set("favicon"sv, JsonValue {});
send_message(move(response));
send_response(message, move(response));
return;
}
@ -46,7 +46,7 @@ void TabActor::handle_message(Message const& message)
response.set("actor"sv, m_watcher->name());
response.set("traits"sv, m_watcher->serialize_description());
send_message(move(response));
send_response(message, move(response));
return;
}

View file

@ -31,7 +31,7 @@ void TargetConfigurationActor::handle_message(Message const& message)
if (!configuration.has_value())
return;
send_message(move(response));
send_response(message, move(response));
return;
}

View file

@ -31,7 +31,7 @@ void ThreadConfigurationActor::handle_message(Message const& message)
if (!configuration.has_value())
return;
send_message(move(response));
send_response(message, move(response));
return;
}

View file

@ -54,7 +54,7 @@ void WalkerActor::handle_message(Message const& message)
auto ancestor_node = WalkerActor::dom_node_for(*this, *node);
if (!ancestor_node.has_value()) {
send_unknown_actor_error(*node);
send_unknown_actor_error(message, *node);
return;
}
@ -69,7 +69,7 @@ void WalkerActor::handle_message(Message const& message)
response.set("hasFirst"sv, !nodes.is_empty());
response.set("hasLast"sv, !nodes.is_empty());
response.set("nodes"sv, move(nodes));
send_message(move(response));
send_response(message, move(response));
return;
}
@ -80,11 +80,11 @@ void WalkerActor::handle_message(Message const& message)
auto dom_node = WalkerActor::dom_node_for(*this, *node);
if (!dom_node.has_value()) {
send_unknown_actor_error(*node);
send_unknown_actor_error(message, *node);
return;
}
devtools().delegate().clone_dom_node(dom_node->tab->description(), dom_node->identifier.id, default_async_handler());
devtools().delegate().clone_dom_node(dom_node->tab->description(), dom_node->identifier.id, default_async_handler(message));
return;
}
@ -99,11 +99,11 @@ void WalkerActor::handle_message(Message const& message)
auto dom_node = WalkerActor::dom_node_for(*this, *node);
if (!dom_node.has_value()) {
send_unknown_actor_error(*node);
send_unknown_actor_error(message, *node);
return;
}
devtools().delegate().set_dom_node_tag(dom_node->tab->description(), dom_node->identifier.id, *tag_name, default_async_handler());
devtools().delegate().set_dom_node_tag(dom_node->tab->description(), dom_node->identifier.id, *tag_name, default_async_handler(message));
return;
}
@ -115,13 +115,13 @@ void WalkerActor::handle_message(Message const& message)
actor.set("actor"sv, m_layout_inspector->name());
response.set("actor"sv, move(actor));
send_message(move(response));
send_response(message, move(response));
return;
}
if (message.type == "getMutations"sv) {
response.set("mutations"sv, serialize_mutations());
send_message(move(response));
send_response(message, move(response));
m_has_new_mutations_since_last_mutations_request = false;
return;
@ -129,7 +129,7 @@ void WalkerActor::handle_message(Message const& message)
if (message.type == "getOffsetParent"sv) {
response.set("node"sv, JsonValue {});
send_message(move(response));
send_response(message, move(response));
return;
}
@ -140,12 +140,12 @@ void WalkerActor::handle_message(Message const& message)
auto dom_node = WalkerActor::dom_node_for(*this, *node);
if (!dom_node.has_value()) {
send_unknown_actor_error(*node);
send_unknown_actor_error(message, *node);
return;
}
devtools().delegate().get_dom_node_inner_html(dom_node->tab->description(), dom_node->identifier.id,
async_handler([](auto&, auto html, auto& response) {
async_handler(message, [](auto&, auto html, auto& response) {
response.set("value"sv, move(html));
}));
@ -162,12 +162,12 @@ void WalkerActor::handle_message(Message const& message)
auto dom_node = WalkerActor::dom_node_for(*this, *node);
if (!dom_node.has_value()) {
send_unknown_actor_error(*node);
send_unknown_actor_error(message, *node);
return;
}
devtools().delegate().create_child_element(dom_node->tab->description(), dom_node->identifier.id,
async_handler<WalkerActor>([](auto& self, auto node_id, auto& response) {
async_handler<WalkerActor>(message, [](auto& self, auto node_id, auto& response) {
JsonArray nodes;
if (auto actor = self.m_dom_node_id_to_actor_map.get(node_id); actor.has_value()) {
@ -193,13 +193,13 @@ void WalkerActor::handle_message(Message const& message)
auto dom_node = WalkerActor::dom_node_for(*this, *node);
if (!dom_node.has_value()) {
send_unknown_actor_error(*node);
send_unknown_actor_error(message, *node);
return;
}
auto parent_dom_node = WalkerActor::dom_node_for(*this, *parent);
if (!parent_dom_node.has_value()) {
send_unknown_actor_error(*parent);
send_unknown_actor_error(message, *parent);
return;
}
@ -207,14 +207,14 @@ void WalkerActor::handle_message(Message const& message)
if (auto sibling = message.data.get_string("sibling"sv); sibling.has_value()) {
auto sibling_dom_node = WalkerActor::dom_node_for(*this, *sibling);
if (!sibling_dom_node.has_value()) {
send_unknown_actor_error(*sibling);
send_unknown_actor_error(message, *sibling);
return;
}
sibling_node_id = sibling_dom_node->identifier.id;
}
devtools().delegate().insert_dom_node_before(dom_node->tab->description(), dom_node->identifier.id, parent_dom_node->identifier.id, sibling_node_id, default_async_handler());
devtools().delegate().insert_dom_node_before(dom_node->tab->description(), dom_node->identifier.id, parent_dom_node->identifier.id, sibling_node_id, default_async_handler(message));
return;
}
@ -224,7 +224,7 @@ void WalkerActor::handle_message(Message const& message)
return;
response.set("attached"sv, m_actor_to_dom_node_map.contains(*node));
send_message(move(response));
send_response(message, move(response));
return;
}
@ -235,12 +235,12 @@ void WalkerActor::handle_message(Message const& message)
auto dom_node = WalkerActor::dom_node_for(*this, *node);
if (!dom_node.has_value()) {
send_unknown_actor_error(*node);
send_unknown_actor_error(message, *node);
return;
}
devtools().delegate().get_dom_node_outer_html(dom_node->tab->description(), dom_node->identifier.id,
async_handler([](auto&, auto html, auto& response) {
async_handler(message, [](auto&, auto html, auto& response) {
response.set("value"sv, move(html));
}));
@ -254,7 +254,7 @@ void WalkerActor::handle_message(Message const& message)
auto dom_node = WalkerActor::dom_node_for(*this, *node);
if (!dom_node.has_value()) {
send_unknown_actor_error(*node);
send_unknown_actor_error(message, *node);
return;
}
@ -263,7 +263,7 @@ void WalkerActor::handle_message(Message const& message)
previous_sibling = serialize_node(*previous_sibling_node);
response.set("node"sv, move(previous_sibling));
send_message(move(response));
send_response(message, move(response));
return;
}
@ -278,7 +278,7 @@ void WalkerActor::handle_message(Message const& message)
auto ancestor_node = WalkerActor::dom_node_for(*this, *node);
if (!ancestor_node.has_value()) {
send_unknown_actor_error(*node);
send_unknown_actor_error(message, *node);
return;
}
@ -294,7 +294,7 @@ void WalkerActor::handle_message(Message const& message)
}
}
send_message(move(response));
send_response(message, move(response));
return;
}
@ -305,7 +305,7 @@ void WalkerActor::handle_message(Message const& message)
auto dom_node = WalkerActor::dom_node_for(*this, *node);
if (!dom_node.has_value()) {
send_unknown_actor_error(*node);
send_unknown_actor_error(message, *node);
return;
}
@ -318,7 +318,7 @@ void WalkerActor::handle_message(Message const& message)
return;
devtools().delegate().remove_dom_node(dom_node->tab->description(), dom_node->identifier.id,
async_handler([next_sibling = move(next_sibling)](auto&, auto, auto& response) mutable {
async_handler(message, [next_sibling = move(next_sibling)](auto&, auto, auto& response) mutable {
response.set("nextSibling"sv, move(next_sibling));
}));
@ -326,7 +326,7 @@ void WalkerActor::handle_message(Message const& message)
}
if (message.type == "retainNode"sv) {
send_message(move(response));
send_response(message, move(response));
return;
}
@ -341,18 +341,18 @@ void WalkerActor::handle_message(Message const& message)
auto dom_node = WalkerActor::dom_node_for(*this, *node);
if (!dom_node.has_value()) {
send_unknown_actor_error(*node);
send_unknown_actor_error(message, *node);
return;
}
devtools().delegate().set_dom_node_outer_html(dom_node->tab->description(), dom_node->identifier.id, value.release_value(), default_async_handler());
devtools().delegate().set_dom_node_outer_html(dom_node->tab->description(), dom_node->identifier.id, value.release_value(), default_async_handler(message));
return;
}
if (message.type == "watchRootNode"sv) {
response.set("type"sv, "root-available"sv);
response.set("node"sv, serialize_root());
send_message(move(response));
send_response(message, move(response));
send_message({});
return;

View file

@ -43,7 +43,7 @@ void WatcherActor::handle_message(Message const& message)
return;
response.set("browsingContextID"sv, *browsing_context_id);
send_message(move(response));
send_response(message, move(response));
return;
}
@ -52,7 +52,7 @@ void WatcherActor::handle_message(Message const& message)
m_target_configuration = devtools().register_actor<TargetConfigurationActor>();
response.set("configuration"sv, m_target_configuration->serialize_configuration());
send_message(move(response));
send_response(message, move(response));
return;
}
@ -61,7 +61,7 @@ void WatcherActor::handle_message(Message const& message)
m_thread_configuration = devtools().register_actor<ThreadConfigurationActor>();
response.set("configuration"sv, m_thread_configuration->serialize_configuration());
send_message(move(response));
send_response(message, move(response));
return;
}
@ -79,7 +79,7 @@ void WatcherActor::handle_message(Message const& message)
}
}
send_message(move(response));
send_response(message, move(response));
return;
}
@ -99,7 +99,7 @@ void WatcherActor::handle_message(Message const& message)
response.set("type"sv, "target-available-form"sv);
response.set("target"sv, target.serialize_target());
send_message(move(response));
send_response(message, move(response));
target.send_frame_update_message();