LibWebView: Integrate the DevTools server into the WebView application

This adds a command line option to enable the DevTools server. Here, we
make the application the DevToolsDelegate to reply to requests from the
DevTools server about the state of the application.
This commit is contained in:
Timothy Flynn 2025-02-15 08:09:47 -05:00 committed by Tim Flynn
commit dba6401260
Notes: github-actions[bot] 2025-02-19 13:47:06 +00:00
4 changed files with 83 additions and 2 deletions

View file

@ -10,8 +10,10 @@
#include <LibCore/StandardPaths.h>
#include <LibCore/System.h>
#include <LibCore/TimeZoneWatcher.h>
#include <LibDevTools/DevToolsServer.h>
#include <LibFileSystem/FileSystem.h>
#include <LibImageDecoderClient/Client.h>
#include <LibWeb/CSS/PropertyID.h>
#include <LibWebView/Application.h>
#include <LibWebView/CookieJar.h>
#include <LibWebView/Database.h>
@ -68,6 +70,7 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
bool allow_popups = false;
bool disable_scripting = false;
bool disable_sql_database = false;
Optional<u16> devtools_port;
Optional<StringView> debug_process;
Optional<StringView> profile_process;
Optional<StringView> webdriver_content_ipc_path;
@ -97,6 +100,7 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
args_parser.add_option(debug_process, "Wait for a debugger to attach to the given process name (WebContent, RequestServer, etc.)", "debug-process", 0, "process-name");
args_parser.add_option(profile_process, "Enable callgrind profiling of the given process name (WebContent, RequestServer, etc.)", "profile-process", 0, "process-name");
args_parser.add_option(webdriver_content_ipc_path, "Path to WebDriver IPC for WebContent", "webdriver-content-path", 0, "path", Core::ArgsParser::OptionHideMode::CommandLineAndMarkdown);
args_parser.add_option(devtools_port, "Set the Firefox DevTools port (EXPERIMENTAL)", "devtools", 0, "port");
args_parser.add_option(log_all_js_exceptions, "Log all JavaScript exceptions", "log-all-js-exceptions");
args_parser.add_option(enable_idl_tracing, "Enable IDL tracing", "enable-idl-tracing");
args_parser.add_option(enable_http_cache, "Enable HTTP cache", "enable-http-cache");
@ -156,6 +160,7 @@ void Application::initialize(Main::Arguments const& arguments, URL::URL new_tab_
? DNSSettings(DNSOverTLS(dns_server_address.release_value(), *dns_server_port))
: DNSSettings(DNSOverUDP(dns_server_address.release_value(), *dns_server_port)))
: SystemDNS {}),
.devtools_port = devtools_port,
};
if (webdriver_content_ipc_path.has_value())
@ -190,6 +195,7 @@ ErrorOr<void> Application::launch_services()
{
TRY(launch_request_server());
TRY(launch_image_decoder_server());
TRY(launch_devtools_server());
return {};
}
@ -228,6 +234,13 @@ ErrorOr<void> Application::launch_image_decoder_server()
return {};
}
ErrorOr<void> Application::launch_devtools_server()
{
if (m_chrome_options.devtools_port.has_value())
m_devtools = TRY(DevTools::DevToolsServer::create(*this, *m_chrome_options.devtools_port));
return {};
}
int Application::execute()
{
int ret = m_event_loop.exec();
@ -315,4 +328,60 @@ ErrorOr<LexicalPath> Application::path_for_downloaded_file(StringView file) cons
return LexicalPath::join(downloads_directory, file);
}
void Application::refresh_tab_list()
{
if (!m_devtools)
return;
m_devtools->refresh_tab_list();
}
Vector<DevTools::TabDescription> Application::tab_list() const
{
Vector<DevTools::TabDescription> tabs;
ViewImplementation::for_each_view([&](ViewImplementation& view) {
tabs.empend(view.view_id(), view.title(), view.url().to_byte_string());
return IterationDecision::Continue;
});
return tabs;
}
Vector<DevTools::CSSProperty> Application::css_property_list() const
{
Vector<DevTools::CSSProperty> property_list;
for (auto i = to_underlying(Web::CSS::first_property_id); i <= to_underlying(Web::CSS::last_property_id); ++i) {
auto property_id = static_cast<Web::CSS::PropertyID>(i);
DevTools::CSSProperty property;
property.name = Web::CSS::string_from_property_id(property_id).to_string().to_byte_string();
property.is_inherited = Web::CSS::is_inherited_property(property_id);
property_list.append(move(property));
}
return property_list;
}
void Application::inspect_tab(DevTools::TabDescription const& description, DevTools::DevToolsDelegate::OnTabInspectionComplete 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_tree = [&view = *view, on_complete = move(on_complete)](ByteString const& 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());
}
};
view->inspect_dom_tree();
}
}