1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 05:38:11 +00:00

LibWeb+WebContent: Do not embed attributes as data in the Inspector HTML

Attribute values may contain HTML, and may contain invalid HTML at that.
If the latter occurs, let's not generate invalid Inspector HTML when we
embed the attribute values as data attributes. Instead, cache the values
in the InspectorClient, and embed just a lookup index into the HTML.

This also nicely reduces the size of the generated HTML. The Inspector
on https://github.com/SerenityOS/serenity reduces from 2.3MB to 1.9MB
(about 318KB, or 13.8%).
This commit is contained in:
Timothy Flynn 2024-02-18 13:59:28 -05:00 committed by Andreas Kling
parent 939779cad3
commit f07f5a2622
13 changed files with 60 additions and 52 deletions

View file

@ -288,8 +288,10 @@ const editDOMNode = domNode => {
const element = document.createElement(value);
inspector.setDOMNodeTag(domNodeID, value);
} else if (type === "attribute") {
const attributeIndex = domNode.dataset.attributeIndex;
const attributes = parseDOMAttributes(value);
inspector.replaceDOMNodeAttribute(domNodeID, domNode.dataset.attributeName, attributes);
inspector.replaceDOMNodeAttribute(domNodeID, attributeIndex, attributes);
}
};
@ -354,28 +356,17 @@ const requestContextMenu = (clientX, clientY, domNode) => {
}
let tag = null;
let attributeName = null;
let attributeValue = null;
let attributeIndex = null;
if (type === "tag") {
tag = domNode.dataset.tag;
} else if (type === "attribute") {
tag = domNode.dataset.tag;
attributeName = domNode.dataset.attributeName;
attributeValue = domNode.dataset.attributeValue;
attributeIndex = domNode.dataset.attributeIndex;
}
pendingEditDOMNode = domNode;
inspector.requestDOMTreeContextMenu(
domNodeID,
clientX,
clientY,
type,
tag,
attributeName,
attributeValue
);
inspector.requestDOMTreeContextMenu(domNodeID, clientX, clientY, type, tag, attributeIndex);
};
const executeConsoleScript = consoleInput => {

View file

@ -61,14 +61,14 @@ void Inspector::add_dom_node_attributes(i32 node_id, JS::NonnullGCPtr<DOM::Named
global_object().browsing_context()->page().client().inspector_did_add_dom_node_attributes(node_id, attributes);
}
void Inspector::replace_dom_node_attribute(i32 node_id, String const& name, JS::NonnullGCPtr<DOM::NamedNodeMap> replacement_attributes)
void Inspector::replace_dom_node_attribute(i32 node_id, WebIDL::UnsignedLongLong attribute_index, JS::NonnullGCPtr<DOM::NamedNodeMap> replacement_attributes)
{
global_object().browsing_context()->page().client().inspector_did_replace_dom_node_attribute(node_id, name, replacement_attributes);
global_object().browsing_context()->page().client().inspector_did_replace_dom_node_attribute(node_id, static_cast<size_t>(attribute_index), replacement_attributes);
}
void Inspector::request_dom_tree_context_menu(i32 node_id, i32 client_x, i32 client_y, String const& type, Optional<String> const& tag, Optional<String> const& attribute_name, Optional<String> const& attribute_value)
void Inspector::request_dom_tree_context_menu(i32 node_id, i32 client_x, i32 client_y, String const& type, Optional<String> const& tag, Optional<WebIDL::UnsignedLongLong> const& attribute_index)
{
global_object().browsing_context()->page().client().inspector_did_request_dom_tree_context_menu(node_id, { client_x, client_y }, type, tag, attribute_name, attribute_value);
global_object().browsing_context()->page().client().inspector_did_request_dom_tree_context_menu(node_id, { client_x, client_y }, type, tag, attribute_index.map([](auto index) { return static_cast<size_t>(index); }));
}
void Inspector::execute_console_script(String const& script)

View file

@ -8,6 +8,7 @@
#include <LibJS/Forward.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/WebIDL/Types.h>
namespace Web::Internals {
@ -24,9 +25,9 @@ public:
void set_dom_node_text(i32 node_id, String const& text);
void set_dom_node_tag(i32 node_id, String const& tag);
void add_dom_node_attributes(i32 node_id, JS::NonnullGCPtr<DOM::NamedNodeMap> attributes);
void replace_dom_node_attribute(i32 node_id, String const& name, JS::NonnullGCPtr<DOM::NamedNodeMap> replacement_attributes);
void replace_dom_node_attribute(i32 node_id, WebIDL::UnsignedLongLong attribute_index, JS::NonnullGCPtr<DOM::NamedNodeMap> replacement_attributes);
void request_dom_tree_context_menu(i32 node_id, i32 client_x, i32 client_y, String const& type, Optional<String> const& tag, Optional<String> const& attribute_name, Optional<String> const& attribute_value);
void request_dom_tree_context_menu(i32 node_id, i32 client_x, i32 client_y, String const& type, Optional<String> const& tag, Optional<WebIDL::UnsignedLongLong> const& attribute_index);
void execute_console_script(String const& script);

View file

@ -8,9 +8,9 @@
undefined setDOMNodeText(long nodeID, DOMString text);
undefined setDOMNodeTag(long nodeID, DOMString tag);
undefined addDOMNodeAttributes(long nodeID, NamedNodeMap attributes);
undefined replaceDOMNodeAttribute(long nodeID, DOMString name, NamedNodeMap replacementAttributes);
undefined replaceDOMNodeAttribute(long nodeID, unsigned long long attributeIndex, NamedNodeMap replacementAttributes);
undefined requestDOMTreeContextMenu(long nodeID, long clientX, long clientY, DOMString type, DOMString? tag, DOMString? attributeName, DOMString? attributeValue);
undefined requestDOMTreeContextMenu(long nodeID, long clientX, long clientY, DOMString type, DOMString? tag, unsigned long long? attributeIndex);
undefined executeConsoleScript(DOMString script);

View file

@ -298,8 +298,8 @@ public:
virtual void inspector_did_set_dom_node_text([[maybe_unused]] i32 node_id, [[maybe_unused]] String const& text) { }
virtual void inspector_did_set_dom_node_tag([[maybe_unused]] i32 node_id, [[maybe_unused]] String const& tag) { }
virtual void inspector_did_add_dom_node_attributes([[maybe_unused]] i32 node_id, [[maybe_unused]] JS::NonnullGCPtr<DOM::NamedNodeMap> attributes) { }
virtual void inspector_did_replace_dom_node_attribute([[maybe_unused]] i32 node_id, [[maybe_unused]] String const& name, [[maybe_unused]] JS::NonnullGCPtr<DOM::NamedNodeMap> replacement_attributes) { }
virtual void inspector_did_request_dom_tree_context_menu([[maybe_unused]] i32 node_id, [[maybe_unused]] CSSPixelPoint position, [[maybe_unused]] String const& type, [[maybe_unused]] Optional<String> const& tag, [[maybe_unused]] Optional<String> const& attribute_name, [[maybe_unused]] Optional<String> const& attribute_value) { }
virtual void inspector_did_replace_dom_node_attribute([[maybe_unused]] i32 node_id, [[maybe_unused]] size_t attribute_index, [[maybe_unused]] JS::NonnullGCPtr<DOM::NamedNodeMap> replacement_attributes) { }
virtual void inspector_did_request_dom_tree_context_menu([[maybe_unused]] i32 node_id, [[maybe_unused]] CSSPixelPoint position, [[maybe_unused]] String const& type, [[maybe_unused]] Optional<String> const& tag, [[maybe_unused]] Optional<size_t> const& attribute_index) { }
virtual void inspector_did_execute_console_script([[maybe_unused]] String const& script) { }
virtual void schedule_repaint() = 0;

View file

@ -96,6 +96,7 @@ InspectorClient::InspectorClient(ViewImplementation& content_web_view, ViewImple
m_content_web_view.on_finshed_editing_dom_node = [this](auto const& node_id) {
m_pending_selection = node_id;
m_dom_tree_loaded = false;
m_dom_node_attributes.clear();
inspect();
};
@ -122,7 +123,11 @@ InspectorClient::InspectorClient(ViewImplementation& content_web_view, ViewImple
m_content_web_view.js_console_request_messages(0);
};
m_inspector_web_view.on_inspector_requested_dom_tree_context_menu = [this](auto node_id, auto position, auto const& type, auto const& tag, auto const& attribute) {
m_inspector_web_view.on_inspector_requested_dom_tree_context_menu = [this](auto node_id, auto position, auto const& type, auto const& tag, auto const& attribute_index) {
Optional<Attribute> attribute;
if (attribute_index.has_value())
attribute = m_dom_node_attributes.get(node_id)->at(*attribute_index);
m_context_menu_data = ContextMenuData { node_id, tag, attribute };
if (type.is_one_of("text"sv, "comment"sv)) {
@ -158,8 +163,9 @@ InspectorClient::InspectorClient(ViewImplementation& content_web_view, ViewImple
m_content_web_view.add_dom_node_attributes(node_id, attributes);
};
m_inspector_web_view.on_inspector_replaced_dom_node_attribute = [this](auto node_id, auto const& name, auto const& replacement_attributes) {
m_content_web_view.replace_dom_node_attribute(node_id, name, replacement_attributes);
m_inspector_web_view.on_inspector_replaced_dom_node_attribute = [this](auto node_id, u32 attribute_index, auto const& replacement_attributes) {
auto const& attribute = m_dom_node_attributes.get(node_id)->at(attribute_index);
m_content_web_view.replace_dom_node_attribute(node_id, attribute.name, replacement_attributes);
};
m_inspector_web_view.on_inspector_executed_console_script = [this](auto const& script) {
@ -201,6 +207,8 @@ void InspectorClient::reset()
m_pending_selection.clear();
m_dom_tree_loaded = false;
m_dom_node_attributes.clear();
m_highest_notified_message_index = -1;
m_highest_received_message_index = -1;
m_waiting_for_messages = false;
@ -461,13 +469,17 @@ String InspectorClient::generate_dom_tree(JsonObject const& dom_tree)
data_attributes.appendff("data-{}=\"{}\"", name, value);
};
i32 node_id = 0;
if (auto pseudo_element = node.get_integer<i32>("pseudo-element"sv); pseudo_element.has_value()) {
append_data_attribute("id"sv, node.get_integer<i32>("parent-id"sv).value());
node_id = node.get_integer<i32>("parent-id"sv).value();
append_data_attribute("pseudo-element"sv, *pseudo_element);
} else {
append_data_attribute("id"sv, node.get_integer<i32>("id"sv).value());
node_id = node.get_integer<i32>("id"sv).value();
}
append_data_attribute("id"sv, node_id);
if (type == "text"sv) {
auto deprecated_text = node.get_byte_string("text"sv).release_value();
deprecated_text = escape_html_entities(deprecated_text);
@ -513,7 +525,7 @@ String InspectorClient::generate_dom_tree(JsonObject const& dom_tree)
}
if (name.equals_ignoring_ascii_case("BODY"sv))
m_body_node_id = node.get_integer<i32>("id"sv).value();
m_body_node_id = node_id;
auto tag = name.to_lowercase();
@ -523,14 +535,17 @@ String InspectorClient::generate_dom_tree(JsonObject const& dom_tree)
if (auto attributes = node.get_object("attributes"sv); attributes.has_value()) {
attributes->for_each_member([&](auto const& name, auto const& value) {
auto& dom_node_attributes = m_dom_node_attributes.ensure(node_id);
auto value_string = value.as_string();
builder.append("&nbsp;"sv);
builder.appendff("<span data-node-type=\"attribute\" data-tag=\"{}\" data-attribute-name=\"{}\" data-attribute-value=\"{}\" class=\"editable\">", tag, name, value_string);
builder.appendff("<span data-node-type=\"attribute\" data-tag=\"{}\" data-attribute-index={} class=\"editable\">", tag, dom_node_attributes.size());
builder.appendff("<span class=\"attribute-name\">{}</span>", name);
builder.append('=');
builder.appendff("<span class=\"attribute-value\">\"{}\"</span>", value_string);
builder.append("</span>"sv);
dom_node_attributes.empend(MUST(String::from_byte_string(name)), MUST(String::from_byte_string(value_string)));
});
}

View file

@ -5,9 +5,12 @@
*/
#include <AK/Function.h>
#include <AK/HashMap.h>
#include <AK/JsonValue.h>
#include <AK/StringView.h>
#include <AK/Vector.h>
#include <LibGfx/Point.h>
#include <LibWebView/Attribute.h>
#include <LibWebView/ViewImplementation.h>
#pragma once
@ -76,6 +79,8 @@ private:
};
Optional<ContextMenuData> m_context_menu_data;
HashMap<int, Vector<Attribute>> m_dom_node_attributes;
i32 m_highest_notified_message_index { -1 };
i32 m_highest_received_message_index { -1 };
bool m_waiting_for_messages { false };

View file

@ -174,8 +174,8 @@ public:
Function<void(i32, String const&)> on_inspector_set_dom_node_text;
Function<void(i32, String const&)> on_inspector_set_dom_node_tag;
Function<void(i32, Vector<Attribute> const&)> on_inspector_added_dom_node_attributes;
Function<void(i32, String const&, Vector<Attribute> const&)> on_inspector_replaced_dom_node_attribute;
Function<void(i32, Gfx::IntPoint, String const&, Optional<String> const&, Optional<Attribute> const&)> on_inspector_requested_dom_tree_context_menu;
Function<void(i32, size_t, Vector<Attribute> const&)> on_inspector_replaced_dom_node_attribute;
Function<void(i32, Gfx::IntPoint, String const&, Optional<String> const&, Optional<size_t> const&)> on_inspector_requested_dom_tree_context_menu;
Function<void(String const&)> on_inspector_executed_console_script;
Function<SocketPair()> on_request_worker_agent;

View file

@ -923,7 +923,7 @@ void WebContentClient::inspector_did_add_dom_node_attributes(u64 page_id, i32 no
view.on_inspector_added_dom_node_attributes(node_id, attributes);
}
void WebContentClient::inspector_did_replace_dom_node_attribute(u64 page_id, i32 node_id, String const& name, Vector<Attribute> const& replacement_attributes)
void WebContentClient::inspector_did_replace_dom_node_attribute(u64 page_id, i32 node_id, size_t attribute_index, Vector<Attribute> const& replacement_attributes)
{
auto maybe_view = m_views.get(page_id);
if (!maybe_view.has_value()) {
@ -933,10 +933,10 @@ void WebContentClient::inspector_did_replace_dom_node_attribute(u64 page_id, i32
auto& view = *maybe_view.value();
if (view.on_inspector_replaced_dom_node_attribute)
view.on_inspector_replaced_dom_node_attribute(node_id, name, replacement_attributes);
view.on_inspector_replaced_dom_node_attribute(node_id, attribute_index, replacement_attributes);
}
void WebContentClient::inspector_did_request_dom_tree_context_menu(u64 page_id, i32 node_id, Gfx::IntPoint position, String const& type, Optional<String> const& tag, Optional<Attribute> const& attribute)
void WebContentClient::inspector_did_request_dom_tree_context_menu(u64 page_id, i32 node_id, Gfx::IntPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index)
{
auto maybe_view = m_views.get(page_id);
if (!maybe_view.has_value()) {
@ -946,7 +946,7 @@ void WebContentClient::inspector_did_request_dom_tree_context_menu(u64 page_id,
auto& view = *maybe_view.value();
if (view.on_inspector_requested_dom_tree_context_menu)
view.on_inspector_requested_dom_tree_context_menu(node_id, view.to_widget_position(position), type, tag, attribute);
view.on_inspector_requested_dom_tree_context_menu(node_id, view.to_widget_position(position), type, tag, attribute_index);
}
void WebContentClient::inspector_did_execute_console_script(u64 page_id, String const& script)

View file

@ -99,8 +99,8 @@ private:
virtual void inspector_did_set_dom_node_text(u64 page_id, i32 node_id, String const& text) override;
virtual void inspector_did_set_dom_node_tag(u64 page_id, i32 node_id, String const& tag) override;
virtual void inspector_did_add_dom_node_attributes(u64 page_id, i32 node_id, Vector<Attribute> const& attributes) override;
virtual void inspector_did_replace_dom_node_attribute(u64 page_id, i32 node_id, String const& name, Vector<Attribute> const& replacement_attributes) override;
virtual void inspector_did_request_dom_tree_context_menu(u64 page_id, i32 node_id, Gfx::IntPoint position, String const& type, Optional<String> const& tag, Optional<Attribute> const& attribute) override;
virtual void inspector_did_replace_dom_node_attribute(u64 page_id, i32 node_id, size_t attribute_index, Vector<Attribute> const& replacement_attributes) override;
virtual void inspector_did_request_dom_tree_context_menu(u64 page_id, i32 node_id, Gfx::IntPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index) override;
virtual void inspector_did_execute_console_script(u64 page_id, String const& script) override;
virtual Messages::WebContentClient::RequestWorkerAgentResponse request_worker_agent(u64 page_id) override;

View file

@ -621,18 +621,14 @@ void PageClient::inspector_did_add_dom_node_attributes(i32 node_id, JS::NonnullG
client().async_inspector_did_add_dom_node_attributes(m_id, node_id, named_node_map_to_vector(attributes));
}
void PageClient::inspector_did_replace_dom_node_attribute(i32 node_id, String const& name, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> replacement_attributes)
void PageClient::inspector_did_replace_dom_node_attribute(i32 node_id, size_t attribute_index, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> replacement_attributes)
{
client().async_inspector_did_replace_dom_node_attribute(m_id, node_id, name, named_node_map_to_vector(replacement_attributes));
client().async_inspector_did_replace_dom_node_attribute(m_id, node_id, attribute_index, named_node_map_to_vector(replacement_attributes));
}
void PageClient::inspector_did_request_dom_tree_context_menu(i32 node_id, Web::CSSPixelPoint position, String const& type, Optional<String> const& tag, Optional<String> const& attribute_name, Optional<String> const& attribute_value)
void PageClient::inspector_did_request_dom_tree_context_menu(i32 node_id, Web::CSSPixelPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index)
{
Optional<WebView::Attribute> attribute;
if (attribute_name.has_value() && attribute_value.has_value())
attribute = WebView::Attribute { *attribute_name, *attribute_value };
client().async_inspector_did_request_dom_tree_context_menu(m_id, node_id, page().css_to_device_point(position).to_type<int>(), type, tag, move(attribute));
client().async_inspector_did_request_dom_tree_context_menu(m_id, node_id, page().css_to_device_point(position).to_type<int>(), type, tag, attribute_index);
}
void PageClient::inspector_did_execute_console_script(String const& script)

View file

@ -144,8 +144,8 @@ private:
virtual void inspector_did_set_dom_node_text(i32 node_id, String const& text) override;
virtual void inspector_did_set_dom_node_tag(i32 node_id, String const& tag) override;
virtual void inspector_did_add_dom_node_attributes(i32 node_id, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> attributes) override;
virtual void inspector_did_replace_dom_node_attribute(i32 node_id, String const& name, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> replacement_attributes) override;
virtual void inspector_did_request_dom_tree_context_menu(i32 node_id, Web::CSSPixelPoint position, String const& type, Optional<String> const& tag, Optional<String> const& attribute_name, Optional<String> const& attribute_value) override;
virtual void inspector_did_replace_dom_node_attribute(i32 node_id, size_t attribute_index, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> replacement_attributes) override;
virtual void inspector_did_request_dom_tree_context_menu(i32 node_id, Web::CSSPixelPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index) override;
virtual void inspector_did_execute_console_script(String const& script) override;
Web::Layout::Viewport* layout_root();

View file

@ -87,8 +87,8 @@ endpoint WebContentClient
inspector_did_set_dom_node_text(u64 page_id, i32 node_id, String text) =|
inspector_did_set_dom_node_tag(u64 page_id, i32 node_id, String tag) =|
inspector_did_add_dom_node_attributes(u64 page_id, i32 node_id, Vector<WebView::Attribute> attributes) =|
inspector_did_replace_dom_node_attribute(u64 page_id, i32 node_id, String name, Vector<WebView::Attribute> replacement_attributes) =|
inspector_did_request_dom_tree_context_menu(u64 page_id, i32 node_id, Gfx::IntPoint position, String type, Optional<String> tag, Optional<WebView::Attribute> attribute) =|
inspector_did_replace_dom_node_attribute(u64 page_id, i32 node_id, size_t attribute_index, Vector<WebView::Attribute> replacement_attributes) =|
inspector_did_request_dom_tree_context_menu(u64 page_id, i32 node_id, Gfx::IntPoint position, String type, Optional<String> tag, Optional<size_t> attribute_index) =|
inspector_did_execute_console_script(u64 page_id, String script) =|
}