diff --git a/Libraries/LibDevTools/Actors/InspectorActor.cpp b/Libraries/LibDevTools/Actors/InspectorActor.cpp index 8ad57617ba9..6bc84bca46c 100644 --- a/Libraries/LibDevTools/Actors/InspectorActor.cpp +++ b/Libraries/LibDevTools/Actors/InspectorActor.cpp @@ -36,7 +36,7 @@ void InspectorActor::handle_message(StringView type, JsonObject const& message) if (type == "getPageStyle"sv) { if (!m_page_style) - m_page_style = devtools().register_actor(); + m_page_style = devtools().register_actor(*this); response.set("pageStyle"sv, m_page_style->serialize_style()); send_message(move(response)); diff --git a/Libraries/LibDevTools/Actors/PageStyleActor.cpp b/Libraries/LibDevTools/Actors/PageStyleActor.cpp index 8d70076d492..0e8d429a012 100644 --- a/Libraries/LibDevTools/Actors/PageStyleActor.cpp +++ b/Libraries/LibDevTools/Actors/PageStyleActor.cpp @@ -5,24 +5,76 @@ */ #include +#include +#include #include +#include +#include +#include +#include namespace DevTools { -NonnullRefPtr PageStyleActor::create(DevToolsServer& devtools, String name) +NonnullRefPtr PageStyleActor::create(DevToolsServer& devtools, String name, WeakPtr inspector) { - return adopt_ref(*new PageStyleActor(devtools, move(name))); + return adopt_ref(*new PageStyleActor(devtools, move(name), move(inspector))); } -PageStyleActor::PageStyleActor(DevToolsServer& devtools, String name) +PageStyleActor::PageStyleActor(DevToolsServer& devtools, String name, WeakPtr inspector) : Actor(devtools, move(name)) + , m_inspector(move(inspector)) { } PageStyleActor::~PageStyleActor() = default; -void PageStyleActor::handle_message(StringView type, JsonObject const&) +void PageStyleActor::handle_message(StringView type, JsonObject const& message) { + JsonObject response; + response.set("from"sv, name()); + + if (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_message(move(response)); + return; + } + + if (type == "getComputed"sv) { + auto node = message.get_string("node"sv); + if (!node.has_value()) { + send_missing_parameter_error("node"sv); + return; + } + + inspect_dom_node(*node, [](auto& self, auto const& properties, auto block_token) { + self.received_computed_style(properties.computed_style, move(block_token)); + }); + + return; + } + + if (type == "getLayout"sv) { + auto node = message.get_string("node"sv); + if (!node.has_value()) { + send_missing_parameter_error("node"sv); + return; + } + + inspect_dom_node(*node, [](auto& self, auto const& properties, auto block_token) { + self.received_layout(properties.computed_style, properties.node_box_sizing, move(block_token)); + }); + + return; + } + + if (type == "isPositionEditable") { + response.set("value"sv, false); + send_message(move(response)); + return; + } + send_unrecognized_packet_type_error(type); } @@ -40,4 +92,95 @@ JsonValue PageStyleActor::serialize_style() const return style; } +template +void PageStyleActor::inspect_dom_node(StringView node_actor, Callback&& callback) +{ + auto tab = InspectorActor::tab_for(m_inspector); + auto walker = InspectorActor::walker_for(m_inspector); + if (!tab || !walker) + return; + + auto const& dom_node = walker->dom_node(node_actor); + if (!dom_node.has_value()) + return; + + auto block_token = block_responses(); + + devtools().delegate().inspect_dom_node( + tab->description(), dom_node->id, dom_node->pseudo_element, + [weak_self = make_weak_ptr(), block_token = move(block_token), callback = forward(callback)](ErrorOr properties) mutable { + if (properties.is_error()) { + dbgln_if(DEVTOOLS_DEBUG, "Unable to inspect DOM node: {}", properties.error()); + return; + } + + if (auto self = weak_self.strong_ref()) + callback(*self, properties.value(), move(block_token)); + }); +} + +void PageStyleActor::received_layout(JsonObject const& computed_style, JsonObject const& node_box_sizing, BlockToken block_token) +{ + JsonObject message; + message.set("from"sv, name()); + message.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) { + message.set(message_key, MUST(String::formatted("{}px", pixel_value(object, object_key)))); + }; + auto set_computed_value_from = [&](auto const& object, auto key) { + message.set(key, object.get_string(key).value_or(String {})); + }; + + message.set("width"sv, pixel_value(node_box_sizing, "content_width"sv)); + message.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); + + send_message(move(message), move(block_token)); +} + +void PageStyleActor::received_computed_style(JsonObject const& computed_style, BlockToken block_token) +{ + 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)); + }); + + JsonObject message; + message.set("from"sv, name()); + message.set("computed"sv, move(computed)); + send_message(move(message), move(block_token)); +} + } diff --git a/Libraries/LibDevTools/Actors/PageStyleActor.h b/Libraries/LibDevTools/Actors/PageStyleActor.h index 03d324f7898..ee5dcbcc6ae 100644 --- a/Libraries/LibDevTools/Actors/PageStyleActor.h +++ b/Libraries/LibDevTools/Actors/PageStyleActor.h @@ -15,14 +15,22 @@ class PageStyleActor final : public Actor { public: static constexpr auto base_name = "page-style"sv; - static NonnullRefPtr create(DevToolsServer&, String name); + static NonnullRefPtr create(DevToolsServer&, String name, WeakPtr); virtual ~PageStyleActor() override; virtual void handle_message(StringView type, JsonObject const&) override; JsonValue serialize_style() const; private: - PageStyleActor(DevToolsServer&, String name); + PageStyleActor(DevToolsServer&, String name, WeakPtr); + + template + void inspect_dom_node(StringView node_actor, Callback&&); + + void received_layout(JsonObject const& computed_style, JsonObject const& node_box_sizing, BlockToken); + void received_computed_style(JsonObject const& computed_style, BlockToken); + + WeakPtr m_inspector; }; } diff --git a/Libraries/LibDevTools/Actors/TabActor.cpp b/Libraries/LibDevTools/Actors/TabActor.cpp index ff690c1681d..f2d185fd097 100644 --- a/Libraries/LibDevTools/Actors/TabActor.cpp +++ b/Libraries/LibDevTools/Actors/TabActor.cpp @@ -76,6 +76,7 @@ JsonObject TabActor::serialize_description() const void TabActor::reset_selected_node() { devtools().delegate().clear_highlighted_dom_node(description()); + devtools().delegate().clear_inspected_dom_node(description()); } } diff --git a/Libraries/LibDevTools/Actors/WalkerActor.cpp b/Libraries/LibDevTools/Actors/WalkerActor.cpp index 9646ad45599..5b605421993 100644 --- a/Libraries/LibDevTools/Actors/WalkerActor.cpp +++ b/Libraries/LibDevTools/Actors/WalkerActor.cpp @@ -72,6 +72,12 @@ void WalkerActor::handle_message(StringView type, JsonObject const& message) return; } + if (type == "getOffsetParent"sv) { + response.set("node"sv, JsonValue {}); + send_message(move(response)); + return; + } + if (type == "querySelector"sv) { auto node = message.get_string("node"sv); if (!node.has_value()) { diff --git a/Libraries/LibDevTools/DevToolsDelegate.h b/Libraries/LibDevTools/DevToolsDelegate.h index 67a066b46cb..6c091b4d54b 100644 --- a/Libraries/LibDevTools/DevToolsDelegate.h +++ b/Libraries/LibDevTools/DevToolsDelegate.h @@ -15,6 +15,7 @@ #include #include #include +#include namespace DevTools { @@ -28,6 +29,10 @@ public: using OnTabInspectionComplete = Function)>; virtual void inspect_tab(TabDescription const&, OnTabInspectionComplete) const { } + using OnDOMNodeInspectionComplete = Function)>; + virtual void inspect_dom_node(TabDescription const&, Web::UniqueNodeID, Optional, OnDOMNodeInspectionComplete) const { } + virtual void clear_inspected_dom_node(TabDescription const&) const { } + virtual void highlight_dom_node(TabDescription const&, Web::UniqueNodeID, Optional) const { } virtual void clear_highlighted_dom_node(TabDescription const&) const { } };