From d4b7dd88b764dc9b2b6dfd836cce3c303fcef871 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Fri, 21 Feb 2025 12:39:43 -0500 Subject: [PATCH] LibWebView: Parse inspector-related JSON strings sooner We currently receive serialized JSON values over IPC and forward them to them WebView callbacks, leaving it to the implementations of those callbacks to parse the strings as JSON objects. This patch hoists that parsing up to WebContentClient as soon as the IPC message is received. This is to reduce the work needed for secondary implementations of these callbacks (i.e. our Firefox DevTools server). --- Libraries/LibWebView/Application.cpp | 9 +--- Libraries/LibWebView/InspectorClient.cpp | 58 +++++------------------ Libraries/LibWebView/ViewImplementation.h | 20 ++++---- Libraries/LibWebView/WebContentClient.cpp | 46 ++++++++++++++---- 4 files changed, 63 insertions(+), 70 deletions(-) diff --git a/Libraries/LibWebView/Application.cpp b/Libraries/LibWebView/Application.cpp index 3f370512243..c3d28b17340 100644 --- a/Libraries/LibWebView/Application.cpp +++ b/Libraries/LibWebView/Application.cpp @@ -371,14 +371,9 @@ void Application::inspect_tab(DevTools::TabDescription const& description, DevTo return; } - view->on_received_dom_tree = [&view = *view, on_complete = move(on_complete)](String const& dom_tree) { + view->on_received_dom_tree = [&view = *view, on_complete = move(on_complete)](JsonObject dom_tree) { view.on_received_dom_tree = nullptr; - - if (auto parsed_tree = JsonValue::from_string(dom_tree); parsed_tree.is_error()) { - on_complete(parsed_tree.release_error()); - } else { - on_complete(parsed_tree.release_value()); - } + on_complete(move(dom_tree)); }; view->inspect_dom_tree(); diff --git a/Libraries/LibWebView/InspectorClient.cpp b/Libraries/LibWebView/InspectorClient.cpp index 1295fcb163e..ef46885e869 100644 --- a/Libraries/LibWebView/InspectorClient.cpp +++ b/Libraries/LibWebView/InspectorClient.cpp @@ -28,15 +28,6 @@ static constexpr auto INSPECTOR_HTML = "resource://ladybird/inspector.html"sv; static constexpr auto INSPECTOR_CSS = "resource://ladybird/inspector.css"sv; static constexpr auto INSPECTOR_JS = "resource://ladybird/inspector.js"sv; -static ErrorOr parse_json_tree(StringView json) -{ - auto parsed_tree = TRY(JsonValue::from_string(json)); - if (!parsed_tree.is_object()) - return Error::from_string_literal("Expected tree to be a JSON object"); - - return parsed_tree; -} - static String style_sheet_identifier_to_json(Web::CSS::StyleSheetIdentifier const& identifier) { return MUST(String::formatted("{{ type: '{}', domNodeId: {}, url: '{}' }}"sv, @@ -50,13 +41,7 @@ InspectorClient::InspectorClient(ViewImplementation& content_web_view, ViewImple , m_inspector_web_view(inspector_web_view) { m_content_web_view.on_received_dom_tree = [this](auto const& dom_tree) { - auto result = parse_json_tree(dom_tree); - if (result.is_error()) { - dbgln("Failed to load DOM tree: {}", result.error()); - return; - } - - auto dom_tree_html = generate_dom_tree(result.value().as_object()); + auto dom_tree_html = generate_dom_tree(dom_tree); auto dom_tree_base64 = MUST(encode_base64(dom_tree_html.bytes())); auto script = MUST(String::formatted("inspector.loadDOMTree(\"{}\");", dom_tree_base64)); @@ -70,44 +55,27 @@ InspectorClient::InspectorClient(ViewImplementation& content_web_view, ViewImple select_default_node(); }; - m_content_web_view.on_received_dom_node_properties = [this](auto const& inspected_node_properties) { + m_content_web_view.on_received_dom_node_properties = [this](auto const& properties) { StringBuilder builder; // FIXME: Support box model metrics and ARIA properties. - auto generate_property_script = [&](auto const& computed_style, auto const& resolved_style, auto const& custom_properties, auto const& fonts) { - builder.append("inspector.createPropertyTables(\""sv); - builder.append_escaped_for_json(computed_style); - builder.append("\", \""sv); - builder.append_escaped_for_json(resolved_style); - builder.append("\", \""sv); - builder.append_escaped_for_json(custom_properties); - builder.append("\");"sv); - builder.append("inspector.createFontList(\""sv); - builder.append_escaped_for_json(fonts); - builder.append("\");"sv); - }; + builder.append("inspector.createPropertyTables(\""sv); + builder.append_escaped_for_json(properties.computed_style.serialized()); + builder.append("\", \""sv); + builder.append_escaped_for_json(properties.resolved_style.serialized()); + builder.append("\", \""sv); + builder.append_escaped_for_json(properties.custom_properties.serialized()); + builder.append("\");"sv); - if (inspected_node_properties.has_value()) { - generate_property_script( - inspected_node_properties->computed_style_json, - inspected_node_properties->resolved_style_json, - inspected_node_properties->custom_properties_json, - inspected_node_properties->fonts_json); - } else { - generate_property_script("{}"sv, "{}"sv, "{}"sv, "{}"sv); - } + builder.append("inspector.createFontList(\""sv); + builder.append_escaped_for_json(properties.fonts.serialized()); + builder.append("\");"sv); m_inspector_web_view.run_javascript(builder.string_view()); }; m_content_web_view.on_received_accessibility_tree = [this](auto const& accessibility_tree) { - auto result = parse_json_tree(accessibility_tree); - if (result.is_error()) { - dbgln("Failed to load accessibility tree: {}", result.error()); - return; - } - - auto accessibility_tree_html = generate_accessibility_tree(result.value().as_object()); + auto accessibility_tree_html = generate_accessibility_tree(accessibility_tree); auto accessibility_tree_base64 = MUST(encode_base64(accessibility_tree_html.bytes())); auto script = MUST(String::formatted("inspector.loadAccessibilityTree(\"{}\");", accessibility_tree_base64)); diff --git a/Libraries/LibWebView/ViewImplementation.h b/Libraries/LibWebView/ViewImplementation.h index 67c6265f1aa..5ec51cbf85e 100644 --- a/Libraries/LibWebView/ViewImplementation.h +++ b/Libraries/LibWebView/ViewImplementation.h @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -35,12 +37,12 @@ public: virtual ~ViewImplementation(); struct DOMNodeProperties { - String computed_style_json; - String resolved_style_json; - String custom_properties_json; - String node_box_sizing_json; - String aria_properties_state_json; - String fonts_json; + 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); @@ -200,9 +202,9 @@ public: Function on_request_accept_dialog; Function on_request_dismiss_dialog; Function on_received_source; - Function on_received_dom_tree; - Function)> on_received_dom_node_properties; - Function on_received_accessibility_tree; + Function on_received_dom_tree; + Function on_received_dom_node_properties; + Function on_received_accessibility_tree; Function)> on_received_style_sheet_list; Function on_inspector_requested_style_sheet_source; Function on_received_style_sheet_source; diff --git a/Libraries/LibWebView/WebContentClient.cpp b/Libraries/LibWebView/WebContentClient.cpp index 0c0cbb37d88..efa09cd418d 100644 --- a/Libraries/LibWebView/WebContentClient.cpp +++ b/Libraries/LibWebView/WebContentClient.cpp @@ -264,11 +264,39 @@ void WebContentClient::did_get_source(u64 page_id, URL::URL const& url, URL::URL } } +template +static JsonType parse_json(StringView json, StringView name) +{ + auto parsed_tree = JsonValue::from_string(json); + if (parsed_tree.is_error()) { + dbgln("Unable to parse {}: {}", name, parsed_tree.error()); + return {}; + } + + if constexpr (IsSame) { + 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) { + 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); + } +} + void WebContentClient::did_inspect_dom_tree(u64 page_id, String const& dom_tree) { if (auto view = view_for_page_id(page_id); view.has_value()) { if (view->on_received_dom_tree) - view->on_received_dom_tree(dom_tree); + view->on_received_dom_tree(parse_json(dom_tree, "DOM tree"sv)); } } @@ -278,16 +306,16 @@ void WebContentClient::did_inspect_dom_node(u64 page_id, bool has_style, String if (!view.has_value() || !view->on_received_dom_node_properties) return; - Optional properties; + ViewImplementation::DOMNodeProperties properties; if (has_style) { properties = ViewImplementation::DOMNodeProperties { - .computed_style_json = computed_style, - .resolved_style_json = resolved_style, - .custom_properties_json = custom_properties, - .node_box_sizing_json = node_box_sizing, - .aria_properties_state_json = aria_properties_state, - .fonts_json = fonts, + .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(fonts, "fonts"sv), }; } @@ -298,7 +326,7 @@ void WebContentClient::did_inspect_accessibility_tree(u64 page_id, String const& { if (auto view = view_for_page_id(page_id); view.has_value()) { if (view->on_received_accessibility_tree) - view->on_received_accessibility_tree(accessibility_tree); + view->on_received_accessibility_tree(parse_json(accessibility_tree, "accessibility tree"sv)); } }