LibWeb+LibWebView+WebContent: Inform the UI about DOM mutations

This will allow our DevTools server to inform the Firefox DevTools
client about DOM mutations.
This commit is contained in:
Timothy Flynn 2025-03-06 17:32:43 -05:00 committed by Andreas Kling
commit 2c4b420acc
Notes: github-actions[bot] 2025-03-08 00:27:41 +00:00
17 changed files with 253 additions and 12 deletions

View file

@ -9,6 +9,7 @@ set(SOURCES
Database.cpp
HelperProcess.cpp
InspectorClient.cpp
Mutation.cpp
Plugins/FontPlugin.cpp
Plugins/ImageCodecPlugin.cpp
Process.cpp

View file

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

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#include <LibWebView/Mutation.h>
template<>
ErrorOr<void> IPC::encode(Encoder& encoder, WebView::AttributeMutation const& mutation)
{
TRY(encoder.encode(mutation.attribute_name));
TRY(encoder.encode(mutation.new_value));
return {};
}
template<>
ErrorOr<WebView::AttributeMutation> IPC::decode(Decoder& decoder)
{
auto attribute_name = TRY(decoder.decode<String>());
auto new_value = TRY(decoder.decode<Optional<String>>());
return WebView::AttributeMutation { move(attribute_name), move(new_value) };
}
template<>
ErrorOr<void> IPC::encode(Encoder& encoder, WebView::CharacterDataMutation const& mutation)
{
TRY(encoder.encode(mutation.new_value));
return {};
}
template<>
ErrorOr<WebView::CharacterDataMutation> IPC::decode(Decoder& decoder)
{
auto new_value = TRY(decoder.decode<String>());
return WebView::CharacterDataMutation { move(new_value) };
}
template<>
ErrorOr<void> IPC::encode(Encoder& encoder, WebView::ChildListMutation const& mutation)
{
TRY(encoder.encode(mutation.added));
TRY(encoder.encode(mutation.removed));
TRY(encoder.encode(mutation.target_child_count));
return {};
}
template<>
ErrorOr<WebView::ChildListMutation> IPC::decode(Decoder& decoder)
{
auto added = TRY(decoder.decode<Vector<Web::UniqueNodeID>>());
auto removed = TRY(decoder.decode<Vector<Web::UniqueNodeID>>());
auto target_child_count = TRY(decoder.decode<size_t>());
return WebView::ChildListMutation { move(added), move(removed), target_child_count };
}
template<>
ErrorOr<void> IPC::encode(Encoder& encoder, WebView::Mutation const& mutation)
{
TRY(encoder.encode(mutation.type));
TRY(encoder.encode(mutation.target));
TRY(encoder.encode(mutation.serialized_target));
TRY(encoder.encode(mutation.mutation));
return {};
}
template<>
ErrorOr<WebView::Mutation> IPC::decode(Decoder& decoder)
{
auto type = TRY(decoder.decode<String>());
auto target = TRY(decoder.decode<Web::UniqueNodeID>());
auto serialized_target = TRY(decoder.decode<String>());
auto mutation = TRY(decoder.decode<WebView::Mutation::Type>());
return WebView::Mutation { move(type), target, move(serialized_target), move(mutation) };
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/String.h>
#include <AK/Variant.h>
#include <AK/Vector.h>
#include <LibIPC/Forward.h>
#include <LibWeb/Forward.h>
namespace WebView {
struct AttributeMutation {
String attribute_name;
Optional<String> new_value;
};
struct CharacterDataMutation {
String new_value;
};
struct ChildListMutation {
Vector<Web::UniqueNodeID> added;
Vector<Web::UniqueNodeID> removed;
size_t target_child_count { 0 };
};
struct Mutation {
using Type = Variant<AttributeMutation, CharacterDataMutation, ChildListMutation>;
String type;
Web::UniqueNodeID target { 0 };
String serialized_target;
Type mutation;
};
}
namespace IPC {
template<>
ErrorOr<void> encode(Encoder&, WebView::AttributeMutation const&);
template<>
ErrorOr<WebView::AttributeMutation> decode(Decoder&);
template<>
ErrorOr<void> encode(Encoder&, WebView::CharacterDataMutation const&);
template<>
ErrorOr<WebView::CharacterDataMutation> decode(Decoder&);
template<>
ErrorOr<void> encode(Encoder&, WebView::ChildListMutation const&);
template<>
ErrorOr<WebView::ChildListMutation> decode(Decoder&);
template<>
ErrorOr<void> encode(Encoder&, WebView::Mutation const&);
template<>
ErrorOr<WebView::Mutation> decode(Decoder&);
}

View file

@ -333,6 +333,11 @@ void ViewImplementation::clear_highlighted_dom_node()
highlight_dom_node(0, {});
}
void ViewImplementation::set_listen_for_dom_mutations(bool listen_for_dom_mutations)
{
client().async_set_listen_for_dom_mutations(page_id(), listen_for_dom_mutations);
}
void ViewImplementation::set_dom_node_text(Web::UniqueNodeID node_id, String text)
{
client().async_set_dom_node_text(page_id(), node_id, move(text));

View file

@ -112,6 +112,7 @@ public:
void highlight_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element);
void clear_highlighted_dom_node();
void set_listen_for_dom_mutations(bool);
void set_dom_node_text(Web::UniqueNodeID node_id, String text);
void set_dom_node_tag(Web::UniqueNodeID node_id, String name);
void add_dom_node_attributes(Web::UniqueNodeID node_id, Vector<Attribute> attributes);
@ -213,6 +214,7 @@ public:
Function<void(Web::CSS::StyleSheetIdentifier const&)> on_inspector_requested_style_sheet_source;
Function<void(Web::CSS::StyleSheetIdentifier const&, URL::URL const&, String const&)> on_received_style_sheet_source;
Function<void(Web::UniqueNodeID)> on_received_hovered_node_id;
Function<void(Mutation)> on_dom_mutation_received;
Function<void(Optional<Web::UniqueNodeID> const& node_id)> on_finshed_editing_dom_node;
Function<void(String const&)> on_received_dom_node_html;
Function<void(JsonValue)> on_received_js_console_result;

View file

@ -341,6 +341,14 @@ void WebContentClient::did_finish_editing_dom_node(u64 page_id, Optional<Web::Un
}
}
void WebContentClient::did_mutate_dom(u64 page_id, Mutation const& mutation)
{
if (auto view = view_for_page_id(page_id); view.has_value()) {
if (view->on_dom_mutation_received)
view->on_dom_mutation_received(move(const_cast<Mutation&>(mutation)));
}
}
void WebContentClient::did_get_dom_node_html(u64 page_id, String const& html)
{
if (auto view = view_for_page_id(page_id); view.has_value()) {

View file

@ -76,6 +76,7 @@ private:
virtual void did_inspect_accessibility_tree(u64 page_id, String const&) override;
virtual void did_get_hovered_node_id(u64 page_id, Web::UniqueNodeID const& node_id) override;
virtual void did_finish_editing_dom_node(u64 page_id, Optional<Web::UniqueNodeID> const& node_id) override;
virtual void did_mutate_dom(u64 page_id, Mutation const&) override;
virtual void did_get_dom_node_html(u64 page_id, String const& html) override;
virtual void did_take_screenshot(u64 page_id, Gfx::ShareableBitmap const& screenshot) override;
virtual void did_get_internal_page_info(u64 page_id, PageInfoType, String const&) override;