From 01c44a5c667372e4bb2d636de9f289f3585cf594 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Mon, 10 Mar 2025 18:15:31 -0400 Subject: [PATCH] LibDevTools+LibWebView+WebContent: Implement getting DOM node inner HTML This is used by DevTools to copy the inner HTML to the clipboard. --- Libraries/LibDevTools/Actors/WalkerActor.cpp | 30 ++++++++++++++++++++ Libraries/LibDevTools/DevToolsDelegate.h | 1 + Libraries/LibWebView/Application.cpp | 16 +++++++++++ Libraries/LibWebView/Application.h | 1 + Libraries/LibWebView/ViewImplementation.cpp | 5 ++++ Libraries/LibWebView/ViewImplementation.h | 1 + Services/WebContent/ConnectionFromClient.cpp | 21 ++++++++++++++ Services/WebContent/ConnectionFromClient.h | 1 + Services/WebContent/WebContentServer.ipc | 1 + 9 files changed, 77 insertions(+) diff --git a/Libraries/LibDevTools/Actors/WalkerActor.cpp b/Libraries/LibDevTools/Actors/WalkerActor.cpp index 6b6789cde0f..49f531969b1 100644 --- a/Libraries/LibDevTools/Actors/WalkerActor.cpp +++ b/Libraries/LibDevTools/Actors/WalkerActor.cpp @@ -161,6 +161,36 @@ void WalkerActor::handle_message(StringView type, JsonObject const& message) return; } + if (type == "innerHTML"sv) { + auto node = message.get_string("node"sv); + if (!node.has_value()) { + send_missing_parameter_error("node"sv); + return; + } + + if (auto dom_node = WalkerActor::dom_node_for(*this, *node); dom_node.has_value()) { + auto block_token = block_responses(); + + devtools().delegate().get_dom_node_inner_html( + dom_node->tab->description(), dom_node->identifier.id, + [weak_self = make_weak_ptr(), block_token = move(block_token)](ErrorOr html) mutable { + if (html.is_error()) { + dbgln_if(DEVTOOLS_DEBUG, "Unable to edit DOM node: {}", html.error()); + return; + } + + if (auto self = weak_self.strong_ref()) { + JsonObject message; + message.set("from"sv, self->name()); + message.set("value"sv, html.release_value()); + self->send_message(move(message), move(block_token)); + } + }); + } + + return; + } + if (type == "insertAdjacentHTML") { // FIXME: This message also contains `value` and `position` parameters, containing the HTML to insert and the // location to insert it. For the "Create New Node" action, this is always "
" and "beforeEnd", diff --git a/Libraries/LibDevTools/DevToolsDelegate.h b/Libraries/LibDevTools/DevToolsDelegate.h index 418602dd6cf..7f5250453d9 100644 --- a/Libraries/LibDevTools/DevToolsDelegate.h +++ b/Libraries/LibDevTools/DevToolsDelegate.h @@ -43,6 +43,7 @@ public: using OnDOMNodeHTMLReceived = Function)>; using OnDOMNodeEditComplete = Function)>; + virtual void get_dom_node_inner_html(TabDescription const&, Web::UniqueNodeID, OnDOMNodeHTMLReceived) const { } virtual void get_dom_node_outer_html(TabDescription const&, Web::UniqueNodeID, OnDOMNodeHTMLReceived) const { } virtual void set_dom_node_outer_html(TabDescription const&, Web::UniqueNodeID, String const&, OnDOMNodeEditComplete) const { } virtual void set_dom_node_text(TabDescription const&, Web::UniqueNodeID, String const&, OnDOMNodeEditComplete) const { } diff --git a/Libraries/LibWebView/Application.cpp b/Libraries/LibWebView/Application.cpp index 7df0bd98d77..4c4ded2059c 100644 --- a/Libraries/LibWebView/Application.cpp +++ b/Libraries/LibWebView/Application.cpp @@ -504,6 +504,22 @@ static void edit_dom_node(DevTools::TabDescription const& description, Applicati edit(*view); } +void Application::get_dom_node_inner_html(DevTools::TabDescription const& description, Web::UniqueNodeID node_id, OnDOMNodeHTMLReceived on_complete) const +{ + auto view = ViewImplementation::find_view_by_id(description.id); + if (!view.has_value()) { + on_complete(Error::from_string_literal("Unable to locate tab")); + return; + } + + view->on_received_dom_node_html = [&view = *view, on_complete = move(on_complete)](auto html) { + view.on_received_dom_node_html = nullptr; + on_complete(html); + }; + + view->get_dom_node_inner_html(node_id); +} + void Application::get_dom_node_outer_html(DevTools::TabDescription const& description, Web::UniqueNodeID node_id, OnDOMNodeHTMLReceived on_complete) const { auto view = ViewImplementation::find_view_by_id(description.id); diff --git a/Libraries/LibWebView/Application.h b/Libraries/LibWebView/Application.h index 6c67bce0cd6..38a1dc52c1a 100644 --- a/Libraries/LibWebView/Application.h +++ b/Libraries/LibWebView/Application.h @@ -100,6 +100,7 @@ private: virtual void clear_highlighted_dom_node(DevTools::TabDescription const&) const override; virtual void listen_for_dom_mutations(DevTools::TabDescription const&, OnDOMMutationReceived) const override; virtual void stop_listening_for_dom_mutations(DevTools::TabDescription const&) const override; + virtual void get_dom_node_inner_html(DevTools::TabDescription const&, Web::UniqueNodeID, OnDOMNodeHTMLReceived) const override; virtual void get_dom_node_outer_html(DevTools::TabDescription const&, Web::UniqueNodeID, OnDOMNodeHTMLReceived) const override; virtual void set_dom_node_outer_html(DevTools::TabDescription const&, Web::UniqueNodeID, String const&, OnDOMNodeEditComplete) const override; virtual void set_dom_node_text(DevTools::TabDescription const&, Web::UniqueNodeID, String const&, OnDOMNodeEditComplete) const override; diff --git a/Libraries/LibWebView/ViewImplementation.cpp b/Libraries/LibWebView/ViewImplementation.cpp index aacaea31d10..913a8ec6476 100644 --- a/Libraries/LibWebView/ViewImplementation.cpp +++ b/Libraries/LibWebView/ViewImplementation.cpp @@ -354,6 +354,11 @@ void ViewImplementation::set_listen_for_dom_mutations(bool listen_for_dom_mutati client().async_set_listen_for_dom_mutations(page_id(), listen_for_dom_mutations); } +void ViewImplementation::get_dom_node_inner_html(Web::UniqueNodeID node_id) +{ + client().async_get_dom_node_inner_html(page_id(), node_id); +} + void ViewImplementation::get_dom_node_outer_html(Web::UniqueNodeID node_id) { client().async_get_dom_node_outer_html(page_id(), node_id); diff --git a/Libraries/LibWebView/ViewImplementation.h b/Libraries/LibWebView/ViewImplementation.h index eb6e322dafa..8631e709e2d 100644 --- a/Libraries/LibWebView/ViewImplementation.h +++ b/Libraries/LibWebView/ViewImplementation.h @@ -115,6 +115,7 @@ public: void clear_highlighted_dom_node(); void set_listen_for_dom_mutations(bool); + void get_dom_node_inner_html(Web::UniqueNodeID node_id); void get_dom_node_outer_html(Web::UniqueNodeID node_id); void set_dom_node_outer_html(Web::UniqueNodeID node_id, String const& html); void set_dom_node_text(Web::UniqueNodeID node_id, String const& text); diff --git a/Services/WebContent/ConnectionFromClient.cpp b/Services/WebContent/ConnectionFromClient.cpp index a1ac9502f24..96beded38f8 100644 --- a/Services/WebContent/ConnectionFromClient.cpp +++ b/Services/WebContent/ConnectionFromClient.cpp @@ -676,6 +676,27 @@ void ConnectionFromClient::set_listen_for_dom_mutations(u64 page_id, bool listen page->page().set_listen_for_dom_mutations(listen_for_dom_mutations); } +void ConnectionFromClient::get_dom_node_inner_html(u64 page_id, Web::UniqueNodeID node_id) +{ + auto* dom_node = Web::DOM::Node::from_unique_id(node_id); + if (!dom_node) + return; + + String html; + + if (dom_node->is_element()) { + auto const& element = static_cast(*dom_node); + html = element.inner_html().release_value_but_fixme_should_propagate_errors(); + } else if (dom_node->is_text() || dom_node->is_comment()) { + auto const& character_data = static_cast(*dom_node); + html = character_data.data(); + } else { + return; + } + + async_did_get_dom_node_html(page_id, html); +} + void ConnectionFromClient::get_dom_node_outer_html(u64 page_id, Web::UniqueNodeID node_id) { auto* dom_node = Web::DOM::Node::from_unique_id(node_id); diff --git a/Services/WebContent/ConnectionFromClient.h b/Services/WebContent/ConnectionFromClient.h index 65d7146ed92..8831734a772 100644 --- a/Services/WebContent/ConnectionFromClient.h +++ b/Services/WebContent/ConnectionFromClient.h @@ -84,6 +84,7 @@ private: virtual void request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier) override; virtual void set_listen_for_dom_mutations(u64 page_id, bool) override; + virtual void get_dom_node_inner_html(u64 page_id, Web::UniqueNodeID node_id) override; virtual void get_dom_node_outer_html(u64 page_id, Web::UniqueNodeID node_id) override; virtual void set_dom_node_outer_html(u64 page_id, Web::UniqueNodeID node_id, String html) override; virtual void set_dom_node_text(u64 page_id, Web::UniqueNodeID node_id, String text) override; diff --git a/Services/WebContent/WebContentServer.ipc b/Services/WebContent/WebContentServer.ipc index ddad965753a..1cac3cb8b33 100644 --- a/Services/WebContent/WebContentServer.ipc +++ b/Services/WebContent/WebContentServer.ipc @@ -57,6 +57,7 @@ endpoint WebContentServer request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier) =| set_listen_for_dom_mutations(u64 page_id, bool listen_for_dom_mutations) =| + get_dom_node_inner_html(u64 page_id, Web::UniqueNodeID node_id) =| get_dom_node_outer_html(u64 page_id, Web::UniqueNodeID node_id) =| set_dom_node_outer_html(u64 page_id, Web::UniqueNodeID node_id, String html) =| set_dom_node_text(u64 page_id, Web::UniqueNodeID node_id, String text) =|