LibDevTools+LibWebView+WebContent: Selectively fetch DOM node properties

When we inspect a DOM node, we currently serialize many properties for
that node, including its layout, computed style, used fonts, etc. Now
that we aren't piggy-backing on the Inspector interface, we can instead
only serialize the specific information required by DevTools.
This commit is contained in:
Timothy Flynn 2025-03-19 15:50:56 -04:00 committed by Jelle Raaijmakers
commit daca9f5995
Notes: github-actions[bot] 2025-03-20 08:02:26 +00:00
18 changed files with 287 additions and 276 deletions

View file

@ -451,25 +451,31 @@ void Application::inspect_tab(DevTools::TabDescription const& description, OnTab
view->inspect_dom_tree();
}
void Application::inspect_dom_node(DevTools::TabDescription const& description, Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element, OnDOMNodeInspectionComplete on_complete) const
void Application::listen_for_dom_properties(DevTools::TabDescription const& description, OnDOMNodePropertiesReceived on_dom_node_properties_received) const
{
auto view = ViewImplementation::find_view_by_id(description.id);
if (!view.has_value()) {
on_complete(Error::from_string_literal("Unable to locate tab"));
if (!view.has_value())
return;
}
view->on_received_dom_node_properties = [&view = *view, on_complete = move(on_complete)](ViewImplementation::DOMNodeProperties properties) {
view.on_received_dom_node_properties = nullptr;
view->on_received_dom_node_properties = move(on_dom_node_properties_received);
}
on_complete(DevTools::DOMNodeProperties {
.computed_style = move(properties.computed_style),
.node_box_sizing = move(properties.node_box_sizing),
.fonts = move(properties.fonts),
});
};
void Application::stop_listening_for_dom_properties(DevTools::TabDescription const& description) const
{
auto view = ViewImplementation::find_view_by_id(description.id);
if (!view.has_value())
return;
view->inspect_dom_node(node_id, pseudo_element);
view->on_received_dom_node_properties = nullptr;
}
void Application::inspect_dom_node(DevTools::TabDescription const& description, DOMNodeProperties::Type property_type, Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element) const
{
auto view = ViewImplementation::find_view_by_id(description.id);
if (!view.has_value())
return;
view->inspect_dom_node(node_id, property_type, pseudo_element);
}
void Application::clear_inspected_dom_node(DevTools::TabDescription const& description) const

View file

@ -97,7 +97,9 @@ private:
virtual Vector<DevTools::TabDescription> tab_list() const override;
virtual Vector<DevTools::CSSProperty> css_property_list() const override;
virtual void inspect_tab(DevTools::TabDescription const&, OnTabInspectionComplete) const override;
virtual void inspect_dom_node(DevTools::TabDescription const&, Web::UniqueNodeID, Optional<Web::CSS::Selector::PseudoElement::Type>, OnDOMNodeInspectionComplete) const override;
virtual void listen_for_dom_properties(DevTools::TabDescription const&, OnDOMNodePropertiesReceived) const override;
virtual void stop_listening_for_dom_properties(DevTools::TabDescription const&) const override;
virtual void inspect_dom_node(DevTools::TabDescription const&, DOMNodeProperties::Type, Web::UniqueNodeID, Optional<Web::CSS::Selector::PseudoElement::Type>) const override;
virtual void clear_inspected_dom_node(DevTools::TabDescription const&) const override;
virtual void highlight_dom_node(DevTools::TabDescription const&, Web::UniqueNodeID, Optional<Web::CSS::Selector::PseudoElement::Type>) const override;
virtual void clear_highlighted_dom_node(DevTools::TabDescription const&) const override;

View file

@ -7,6 +7,7 @@ set(SOURCES
ConsoleOutput.cpp
CookieJar.cpp
Database.cpp
DOMNodeProperties.cpp
HelperProcess.cpp
Mutation.cpp
Plugins/FontPlugin.cpp

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#include <LibWebView/DOMNodeProperties.h>
template<>
ErrorOr<void> IPC::encode(Encoder& encoder, WebView::DOMNodeProperties const& attribute)
{
TRY(encoder.encode(attribute.type));
TRY(encoder.encode(attribute.properties));
return {};
}
template<>
ErrorOr<WebView::DOMNodeProperties> IPC::decode(Decoder& decoder)
{
auto type = TRY(decoder.decode<WebView::DOMNodeProperties::Type>());
auto properties = TRY(decoder.decode<JsonValue>());
return WebView::DOMNodeProperties { type, move(properties) };
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/JsonValue.h>
#include <LibIPC/Forward.h>
namespace WebView {
struct DOMNodeProperties {
enum class Type {
ComputedStyle,
Layout,
UsedFonts,
};
Type type { Type::ComputedStyle };
JsonValue properties;
};
}
namespace IPC {
template<>
ErrorOr<void> encode(Encoder&, WebView::DOMNodeProperties const&);
template<>
ErrorOr<WebView::DOMNodeProperties> decode(Decoder&);
}

View file

@ -21,6 +21,7 @@ class WebContentClient;
struct Attribute;
struct ConsoleOutput;
struct CookieStorageKey;
struct DOMNodeProperties;
struct Mutation;
struct ProcessHandle;
struct SearchEngine;

View file

@ -329,14 +329,14 @@ void ViewImplementation::get_hovered_node_id()
client().async_get_hovered_node_id(page_id());
}
void ViewImplementation::inspect_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element)
void ViewImplementation::inspect_dom_node(Web::UniqueNodeID node_id, DOMNodeProperties::Type property_type, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element)
{
client().async_inspect_dom_node(page_id(), node_id, pseudo_element);
client().async_inspect_dom_node(page_id(), property_type, node_id, pseudo_element);
}
void ViewImplementation::clear_inspected_dom_node()
{
inspect_dom_node(0, {});
client().async_clear_inspected_dom_node(page_id());
}
void ViewImplementation::highlight_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element)

View file

@ -9,7 +9,6 @@
#include <AK/Forward.h>
#include <AK/Function.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/LexicalPath.h>
#include <AK/Queue.h>
@ -26,6 +25,7 @@
#include <LibWeb/HTML/SelectItem.h>
#include <LibWeb/Page/EventResult.h>
#include <LibWeb/Page/InputEvent.h>
#include <LibWebView/DOMNodeProperties.h>
#include <LibWebView/Forward.h>
#include <LibWebView/PageInfo.h>
#include <LibWebView/WebContentClient.h>
@ -36,15 +36,6 @@ class ViewImplementation {
public:
virtual ~ViewImplementation();
struct DOMNodeProperties {
JsonObject computed_style;
JsonObject resolved_style;
JsonObject custom_properties;
JsonObject node_box_sizing;
JsonObject aria_properties_state;
JsonArray fonts;
};
static void for_each_view(Function<IterationDecision(ViewImplementation&)>);
static Optional<ViewImplementation&> find_view_by_id(u64);
@ -108,7 +99,7 @@ public:
void inspect_accessibility_tree();
void get_hovered_node_id();
void inspect_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element);
void inspect_dom_node(Web::UniqueNodeID node_id, DOMNodeProperties::Type, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element);
void clear_inspected_dom_node();
void highlight_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element);

View file

@ -277,8 +277,7 @@ void WebContentClient::did_get_source(u64 page_id, URL::URL url, URL::URL base_u
}
}
template<typename JsonType = JsonObject>
static JsonType parse_json(StringView json, StringView name)
static JsonObject parse_json(StringView json, StringView name)
{
auto parsed_tree = JsonValue::from_string(json);
if (parsed_tree.is_error()) {
@ -286,23 +285,12 @@ static JsonType parse_json(StringView json, StringView name)
return {};
}
if constexpr (IsSame<JsonType, JsonObject>) {
if (!parsed_tree.value().is_object()) {
dbgln("Expected {} to be an object: {}", name, parsed_tree.value());
return {};
}
return move(parsed_tree.release_value().as_object());
} else if constexpr (IsSame<JsonType, JsonArray>) {
if (!parsed_tree.value().is_array()) {
dbgln("Expected {} to be an array: {}", name, parsed_tree.value());
return {};
}
return move(parsed_tree.release_value().as_array());
} else {
static_assert(DependentFalse<JsonType>);
if (!parsed_tree.value().is_object()) {
dbgln("Expected {} to be an object: {}", name, parsed_tree.value());
return {};
}
return move(parsed_tree.release_value().as_object());
}
void WebContentClient::did_inspect_dom_tree(u64 page_id, String dom_tree)
@ -313,26 +301,12 @@ void WebContentClient::did_inspect_dom_tree(u64 page_id, String dom_tree)
}
}
void WebContentClient::did_inspect_dom_node(u64 page_id, bool has_style, String computed_style, String resolved_style, String custom_properties, String node_box_sizing, String aria_properties_state, String fonts)
void WebContentClient::did_inspect_dom_node(u64 page_id, DOMNodeProperties properties)
{
auto view = view_for_page_id(page_id);
if (!view.has_value() || !view->on_received_dom_node_properties)
return;
ViewImplementation::DOMNodeProperties properties;
if (has_style) {
properties = ViewImplementation::DOMNodeProperties {
.computed_style = parse_json(computed_style, "computed style"sv),
.resolved_style = parse_json(resolved_style, "resolved style"sv),
.custom_properties = parse_json(custom_properties, "custom properties"sv),
.node_box_sizing = parse_json(node_box_sizing, "node box sizing"sv),
.aria_properties_state = parse_json(aria_properties_state, "aria properties state"sv),
.fonts = parse_json<JsonArray>(fonts, "fonts"sv),
};
if (auto view = view_for_page_id(page_id); view.has_value()) {
if (view->on_received_dom_node_properties)
view->on_received_dom_node_properties(move(properties));
}
view->on_received_dom_node_properties(move(properties));
}
void WebContentClient::did_inspect_accessibility_tree(u64 page_id, String accessibility_tree)

View file

@ -77,7 +77,7 @@ private:
virtual void did_request_media_context_menu(u64 page_id, Gfx::IntPoint, ByteString, unsigned, Web::Page::MediaContextMenu) override;
virtual void did_get_source(u64 page_id, URL::URL, URL::URL, String) override;
virtual void did_inspect_dom_tree(u64 page_id, String) override;
virtual void did_inspect_dom_node(u64 page_id, bool has_style, String computed_style, String resolved_style, String custom_properties, String node_box_sizing, String aria_properties_state, String fonts) override;
virtual void did_inspect_dom_node(u64 page_id, DOMNodeProperties) override;
virtual void did_inspect_accessibility_tree(u64 page_id, String) override;
virtual void did_get_hovered_node_id(u64 page_id, Web::UniqueNodeID node_id) override;
virtual void did_finish_editing_dom_node(u64 page_id, Optional<Web::UniqueNodeID> node_id) override;