DevTools+WebContent: Support the flexbox highlighter

This commit is contained in:
Sam Atkins 2026-05-27 17:12:14 +01:00
parent 5b2c649eda
commit 2000fd38c1
11 changed files with 169 additions and 2 deletions

View file

@ -31,7 +31,10 @@ HighlighterActor::~HighlighterActor() = default;
void HighlighterActor::clear_current_highlight()
{
if (auto tab = InspectorActor::tab_for(m_inspector)) {
if (m_type_name == "CssGridHighlighter"sv) {
if (m_type_name == "FlexboxHighlighter"sv) {
if (m_highlighted_flexbox_node_id.has_value())
devtools().delegate().clear_flexbox_highlight(tab->description(), *m_highlighted_flexbox_node_id);
} else if (m_type_name == "CssGridHighlighter"sv) {
if (m_highlighted_grid_node_id.has_value())
devtools().delegate().clear_grid_highlight(tab->description(), *m_highlighted_grid_node_id);
} else if (m_is_highlighting_dom_node) {
@ -39,6 +42,7 @@ void HighlighterActor::clear_current_highlight()
}
}
m_highlighted_flexbox_node_id = {};
m_highlighted_grid_node_id = {};
m_is_highlighting_dom_node = false;
}
@ -55,7 +59,14 @@ void HighlighterActor::handle_message(Message const& message)
response.set("value"sv, false);
if (auto dom_node = WalkerActor::dom_node_for(InspectorActor::walker_for(m_inspector), *node); dom_node.has_value()) {
if (m_type_name == "CssGridHighlighter"sv) {
if (m_type_name == "FlexboxHighlighter"sv) {
if (m_highlighted_flexbox_node_id.has_value() && *m_highlighted_flexbox_node_id != dom_node->identifier.id)
devtools().delegate().clear_flexbox_highlight(dom_node->tab->description(), *m_highlighted_flexbox_node_id);
auto options = message.data.get("options"sv).value_or(JsonObject {});
devtools().delegate().highlight_flexbox(dom_node->tab->description(), dom_node->identifier.id, move(options));
m_highlighted_flexbox_node_id = dom_node->identifier.id;
} else if (m_type_name == "CssGridHighlighter"sv) {
if (m_highlighted_grid_node_id.has_value() && *m_highlighted_grid_node_id != dom_node->identifier.id)
devtools().delegate().clear_grid_highlight(dom_node->tab->description(), *m_highlighted_grid_node_id);

View file

@ -32,6 +32,7 @@ private:
WeakPtr<InspectorActor> m_inspector;
String m_type_name;
Optional<Web::UniqueNodeID> m_highlighted_flexbox_node_id;
Optional<Web::UniqueNodeID> m_highlighted_grid_node_id;
bool m_is_highlighting_dom_node { false };
};

View file

@ -56,6 +56,8 @@ public:
virtual void highlight_dom_node(TabDescription const&, Web::UniqueNodeID, Optional<Web::CSS::PseudoElement>) const { }
virtual void clear_highlighted_dom_node(TabDescription const&) const { }
virtual void highlight_flexbox(TabDescription const&, Web::UniqueNodeID, JsonValue) const { }
virtual void clear_flexbox_highlight(TabDescription const&, Web::UniqueNodeID) const { }
virtual void highlight_grid(TabDescription const&, Web::UniqueNodeID, JsonValue) const { }
virtual void clear_grid_highlight(TabDescription const&, Web::UniqueNodeID) const { }

View file

@ -1834,6 +1834,18 @@ void Application::clear_highlighted_dom_node(DevTools::TabDescription const& des
view->clear_highlighted_dom_node();
}
void Application::highlight_flexbox(DevTools::TabDescription const& description, Web::UniqueNodeID node_id, JsonValue options) const
{
if (auto view = ViewImplementation::find_view_by_id(description.id); view.has_value())
view->highlight_flexbox(node_id, move(options));
}
void Application::clear_flexbox_highlight(DevTools::TabDescription const& description, Web::UniqueNodeID node_id) const
{
if (auto view = ViewImplementation::find_view_by_id(description.id); view.has_value())
view->clear_flexbox_highlight(node_id);
}
void Application::highlight_grid(DevTools::TabDescription const& description, Web::UniqueNodeID node_id, JsonValue options) const
{
if (auto view = ViewImplementation::find_view_by_id(description.id); view.has_value())

View file

@ -263,6 +263,8 @@ private:
virtual void inspect_current_flexbox(DevTools::TabDescription const&, Web::UniqueNodeID, bool, OnCurrentFlexboxReceived) const override;
virtual void highlight_dom_node(DevTools::TabDescription const&, Web::UniqueNodeID, Optional<Web::CSS::PseudoElement>) const override;
virtual void clear_highlighted_dom_node(DevTools::TabDescription const&) const override;
virtual void highlight_flexbox(DevTools::TabDescription const&, Web::UniqueNodeID, JsonValue) const override;
virtual void clear_flexbox_highlight(DevTools::TabDescription const&, Web::UniqueNodeID) const override;
virtual void highlight_grid(DevTools::TabDescription const&, Web::UniqueNodeID, JsonValue) const override;
virtual void clear_grid_highlight(DevTools::TabDescription const&, Web::UniqueNodeID) const override;
virtual void listen_for_dom_mutations(DevTools::TabDescription const&, OnDOMMutationReceived) const override;

View file

@ -500,6 +500,16 @@ void ViewImplementation::clear_highlighted_dom_node()
highlight_dom_node(0, {});
}
void ViewImplementation::highlight_flexbox(Web::UniqueNodeID node_id, JsonValue options)
{
client().async_highlight_flexbox(page_id(), node_id, move(options));
}
void ViewImplementation::clear_flexbox_highlight(Web::UniqueNodeID node_id)
{
client().async_clear_flexbox_highlight(page_id(), node_id);
}
void ViewImplementation::highlight_grid(Web::UniqueNodeID node_id, JsonValue options)
{
client().async_highlight_grid(page_id(), node_id, move(options));

View file

@ -129,6 +129,8 @@ public:
void highlight_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::PseudoElement> pseudo_element);
void clear_highlighted_dom_node();
void highlight_flexbox(Web::UniqueNodeID node_id, JsonValue options);
void clear_flexbox_highlight(Web::UniqueNodeID node_id);
void highlight_grid(Web::UniqueNodeID node_id, JsonValue options);
void clear_grid_highlight(Web::UniqueNodeID node_id);

View file

@ -62,6 +62,7 @@
#include <LibWeb/Loader/ResourceLoader.h>
#include <LibWeb/Loader/UserAgent.h>
#include <LibWeb/Namespace.h>
#include <LibWeb/Painting/FlexboxInspectorOverlay.h>
#include <LibWeb/Painting/StackingContext.h>
#include <LibWeb/Painting/ViewportPaintable.h>
#include <LibWeb/PermissionsPolicy/AutoplayAllowlist.h>
@ -988,6 +989,21 @@ void ConnectionFromClient::highlight_dom_node(u64 page_id, Web::UniqueNodeID nod
node->document().set_highlighted_node(node, pseudo_element);
}
static Web::Painting::FlexboxInspectorOverlayOptions flexbox_inspector_overlay_options_from_json(JsonValue const& options)
{
Web::Painting::FlexboxInspectorOverlayOptions result;
if (options.is_object()) {
auto const& object = options.as_object();
if (auto color = object.get_string("color"sv); color.has_value()) {
if (auto parsed_color = Gfx::Color::from_string(*color); parsed_color.has_value())
result.color = *parsed_color;
}
}
return result;
}
static Web::Painting::GridInspectorOverlayOptions grid_inspector_overlay_options_from_json(JsonValue const& options)
{
Web::Painting::GridInspectorOverlayOptions result;
@ -1011,6 +1027,38 @@ static Web::Painting::GridInspectorOverlayOptions grid_inspector_overlay_options
return result;
}
void ConnectionFromClient::highlight_flexbox(u64 page_id, Web::UniqueNodeID node_id, JsonValue options)
{
auto page = this->page(page_id);
if (!page.has_value())
return;
auto* node = Web::DOM::Node::from_unique_id(node_id);
if (!node || !node->layout_node())
return;
node->document().set_flexbox_highlighted_node(node, flexbox_inspector_overlay_options_from_json(options));
}
void ConnectionFromClient::clear_flexbox_highlight(u64 page_id, Web::UniqueNodeID node_id)
{
auto page = this->page(page_id);
if (!page.has_value())
return;
if (node_id != 0) {
auto* node = Web::DOM::Node::from_unique_id(node_id);
if (node)
node->document().clear_flexbox_highlighted_node(node);
return;
}
for (auto& navigable : Web::HTML::all_navigables()) {
if (navigable->active_document())
navigable->active_document()->clear_flexbox_highlighted_node(nullptr);
}
}
void ConnectionFromClient::highlight_grid(u64 page_id, Web::UniqueNodeID node_id, JsonValue options)
{
auto page = this->page(page_id);

View file

@ -96,6 +96,8 @@ private:
virtual void inspect_current_flexbox(u64 page_id, Web::UniqueNodeID node_id, bool only_look_at_parents) override;
virtual void clear_inspected_dom_node(u64 page_id) override;
virtual void highlight_dom_node(u64 page_id, Web::UniqueNodeID node_id, Optional<Web::CSS::PseudoElement> pseudo_element) override;
virtual void highlight_flexbox(u64 page_id, Web::UniqueNodeID node_id, JsonValue options) override;
virtual void clear_flexbox_highlight(u64 page_id, Web::UniqueNodeID node_id) override;
virtual void highlight_grid(u64 page_id, Web::UniqueNodeID node_id, JsonValue options) override;
virtual void clear_grid_highlight(u64 page_id, Web::UniqueNodeID node_id) override;
virtual void inspect_accessibility_tree(u64 page_id) override;

View file

@ -63,6 +63,8 @@ endpoint WebContentServer
inspect_current_flexbox(u64 page_id, Web::UniqueNodeID node_id, bool only_look_at_parents) =|
clear_inspected_dom_node(u64 page_id) =|
highlight_dom_node(u64 page_id, Web::UniqueNodeID node_id, Optional<Web::CSS::PseudoElement> pseudo_element) =|
highlight_flexbox(u64 page_id, Web::UniqueNodeID node_id, JsonValue options) =|
clear_flexbox_highlight(u64 page_id, Web::UniqueNodeID node_id) =|
highlight_grid(u64 page_id, Web::UniqueNodeID node_id, JsonValue options) =|
clear_grid_highlight(u64 page_id, Web::UniqueNodeID node_id) =|
inspect_accessibility_tree(u64 page_id) =|

View file

@ -428,6 +428,19 @@ public:
last_highlighted_pseudo_element = pseudo_element;
}
virtual void highlight_flexbox(DevTools::TabDescription const&, Web::UniqueNodeID node_id, JsonValue options) const override
{
++highlight_flexbox_call_count;
last_highlighted_flexbox_node = node_id;
last_flexbox_highlight_options = move(options);
}
virtual void clear_flexbox_highlight(DevTools::TabDescription const&, Web::UniqueNodeID node_id) const override
{
++clear_flexbox_highlight_call_count;
last_cleared_flexbox_node = node_id;
}
virtual void highlight_grid(DevTools::TabDescription const&, Web::UniqueNodeID node_id, JsonValue options) const override
{
++highlight_grid_call_count;
@ -690,6 +703,8 @@ public:
mutable size_t inspect_current_flexbox_call_count { 0 };
mutable size_t highlight_dom_node_call_count { 0 };
mutable size_t clear_highlighted_dom_node_call_count { 0 };
mutable size_t highlight_flexbox_call_count { 0 };
mutable size_t clear_flexbox_highlight_call_count { 0 };
mutable size_t highlight_grid_call_count { 0 };
mutable size_t clear_grid_highlight_call_count { 0 };
mutable size_t listen_for_dom_mutations_call_count { 0 };
@ -726,6 +741,9 @@ public:
mutable Optional<Web::UniqueNodeID> last_current_grid_node;
mutable Optional<Web::UniqueNodeID> last_current_flexbox_node;
mutable bool last_current_flexbox_only_look_at_parents { false };
mutable Optional<Web::UniqueNodeID> last_highlighted_flexbox_node;
mutable Optional<Web::UniqueNodeID> last_cleared_flexbox_node;
mutable JsonValue last_flexbox_highlight_options;
mutable Optional<Web::UniqueNodeID> last_highlighted_grid_node;
mutable Optional<Web::UniqueNodeID> last_cleared_grid_node;
mutable JsonValue last_grid_highlight_options;
@ -1192,6 +1210,63 @@ TEST_CASE(inspector_walker_highlighter_layout_and_editing)
EXPECT_EQ(session->delegate.clear_grid_highlight_call_count, 3u);
EXPECT_EQ(session->delegate.last_cleared_grid_node.value(), 4u);
JsonObject flexbox_highlighter_request;
flexbox_highlighter_request.set("to"sv, inspector_actor);
flexbox_highlighter_request.set("type"sv, "getHighlighterByType"sv);
flexbox_highlighter_request.set("typeName"sv, "FlexboxHighlighter"sv);
auto flexbox_highlighter_actor = client.request(move(flexbox_highlighter_request)).get_object("highlighter"sv)->get_string("actor"sv).release_value();
JsonObject second_flexbox_highlighter_request;
second_flexbox_highlighter_request.set("to"sv, inspector_actor);
second_flexbox_highlighter_request.set("type"sv, "getHighlighterByType"sv);
second_flexbox_highlighter_request.set("typeName"sv, "FlexboxHighlighter"sv);
auto second_flexbox_highlighter_actor = client.request(move(second_flexbox_highlighter_request))
.get_object("highlighter"sv)
->get_string("actor"sv)
.release_value();
EXPECT_NE(second_flexbox_highlighter_actor, flexbox_highlighter_actor);
JsonObject flexbox_options;
flexbox_options.set("color"sv, "#9400ff"sv);
JsonObject show_flexbox_highlighter;
show_flexbox_highlighter.set("to"sv, flexbox_highlighter_actor);
show_flexbox_highlighter.set("type"sv, "show"sv);
show_flexbox_highlighter.set("node"sv, flex_actor);
show_flexbox_highlighter.set("options"sv, move(flexbox_options));
EXPECT(client.request(move(show_flexbox_highlighter)).get_bool("value"sv).value());
EXPECT_EQ(session->delegate.highlight_flexbox_call_count, 1u);
EXPECT_EQ(session->delegate.last_highlighted_flexbox_node.value(), 10u);
EXPECT_EQ(session->delegate.last_flexbox_highlight_options.as_object().get_string("color"sv).value(), "#9400ff"sv);
EXPECT_EQ(session->delegate.clear_flexbox_highlight_call_count, 0u);
JsonObject show_second_flexbox_highlighter;
show_second_flexbox_highlighter.set("to"sv, second_flexbox_highlighter_actor);
show_second_flexbox_highlighter.set("type"sv, "show"sv);
show_second_flexbox_highlighter.set("node"sv, first_flex_item_actor);
EXPECT(client.request(move(show_second_flexbox_highlighter)).get_bool("value"sv).value());
EXPECT_EQ(session->delegate.clear_flexbox_highlight_call_count, 0u);
EXPECT_EQ(session->delegate.highlight_flexbox_call_count, 2u);
EXPECT_EQ(session->delegate.last_highlighted_flexbox_node.value(), 11u);
EXPECT_EQ(client.request(flexbox_highlighter_actor, "hide"sv).get_string("from"sv).value(), flexbox_highlighter_actor);
EXPECT_EQ(session->delegate.clear_flexbox_highlight_call_count, 1u);
EXPECT_EQ(session->delegate.last_cleared_flexbox_node.value(), 10u);
JsonObject retarget_second_flexbox_highlighter;
retarget_second_flexbox_highlighter.set("to"sv, second_flexbox_highlighter_actor);
retarget_second_flexbox_highlighter.set("type"sv, "show"sv);
retarget_second_flexbox_highlighter.set("node"sv, flex_actor);
EXPECT(client.request(move(retarget_second_flexbox_highlighter)).get_bool("value"sv).value());
EXPECT_EQ(session->delegate.clear_flexbox_highlight_call_count, 2u);
EXPECT_EQ(session->delegate.last_cleared_flexbox_node.value(), 11u);
EXPECT_EQ(session->delegate.highlight_flexbox_call_count, 3u);
EXPECT_EQ(session->delegate.last_highlighted_flexbox_node.value(), 10u);
EXPECT_EQ(client.request(second_flexbox_highlighter_actor, "release"sv).get_string("from"sv).value(), second_flexbox_highlighter_actor);
EXPECT_EQ(session->delegate.clear_flexbox_highlight_call_count, 3u);
EXPECT_EQ(session->delegate.last_cleared_flexbox_node.value(), 10u);
auto layout_actor = client.request(walker_actor, "getLayoutInspector"sv).get_object("actor"sv)->get_string("actor"sv).release_value();
JsonObject get_grids;