LibURL+LibWeb+LibWebView: Convert about:version to a proper WebUI

Passing the browser command line and executable path to every WebContent
process just in case we load about:version always felt a bit weird. We
now use the WebUI framework to load this information on demand.
This commit is contained in:
Timothy Flynn
2026-04-20 17:27:15 -04:00
committed by Tim Flynn
parent 5c835157c6
commit 06796f5f7f
Notes: github-actions[bot] 2026-04-21 11:00:17 +00:00
15 changed files with 115 additions and 86 deletions

View File

@@ -3,7 +3,6 @@
<head>
<meta charset="UTF-8" />
<meta name="color-scheme" content="light dark" />
<title>About %browser_name%</title>
<link rel="stylesheet" type="text/css" href="resource://ladybird/ladybird.css" />
<link rel="stylesheet" type="text/css" href="resource://ladybird/about-pages/webui.css" />
<style>
@@ -116,36 +115,27 @@
<source srcset="resource://icons/128x128/app-browser.png" media="(prefers-color-scheme: dark)" />
<img src="resource://icons/128x128/app-browser-dark.png" />
</picture>
<h1>About %browser_name%</h1>
<h1 id="browser-name"></h1>
</header>
<div class="card summary-card">
<div class="card-body">
<picture>
<source srcset="resource://icons/128x128/app-browser.png" media="(prefers-color-scheme: dark)" />
<img src="resource://icons/128x128/app-browser-dark.png" />
</picture>
<div>
<h2 class="summary-title">%browser_name%</h2>
<p class="summary-version">Version %browser_version%</p>
</div>
</div>
</div>
<h3 class="card-title">Build Details</h3>
<div class="card">
<div class="card-body details">
<div class="detail-row">
<div class="detail-label">Version</div>
<div id="browser-version" class="detail-value"></div>
</div>
<div class="detail-row">
<div class="detail-label">Architecture</div>
<div class="detail-value">%arch_name%</div>
<div id="arch" class="detail-value"></div>
</div>
<div class="detail-row">
<div class="detail-label">Operating System</div>
<div class="detail-value">%os_name%</div>
<div id="platform-name" class="detail-value"></div>
</div>
<div class="detail-row">
<div class="detail-label">User Agent</div>
<div class="detail-value">%user_agent%</div>
<div id="user-agent" class="detail-value"></div>
</div>
</div>
</div>
@@ -155,13 +145,44 @@
<div class="card-body details">
<div class="detail-row">
<div class="detail-label">Command Line</div>
<div class="detail-value">%command_line%</div>
<div id="command-line" class="detail-value"></div>
</div>
<div class="detail-row">
<div class="detail-label">Executable Path</div>
<div class="detail-value">%executable_path%</div>
<div id="executable-path" class="detail-value"></div>
</div>
</div>
</div>
<script type="module">
const browserName = document.querySelector("#browser-name");
const browserVersion = document.querySelector("#browser-version");
const arch = document.querySelector("#arch");
const platformName = document.querySelector("#platform-name");
const userAgent = document.querySelector("#user-agent");
const commandLine = document.querySelector("#command-line");
const executablePath = document.querySelector("#executable-path");
function renderVersionInfo(versionInfo) {
document.title = `About ${versionInfo?.browserName}`;
browserName.innerText = document.title;
browserVersion.innerText = versionInfo?.browserVersion;
arch.innerText = versionInfo?.arch;
platformName.innerText = versionInfo?.platformName;
userAgent.innerText = navigator.userAgent;
commandLine.innerText = versionInfo?.commandLine;
executablePath.innerText = versionInfo?.executablePath;
}
document.addEventListener("WebUILoaded", () => {
ladybird.sendMessage("loadVersionInfo");
});
document.addEventListener("WebUIMessage", event => {
if (event.detail.name === "renderVersionInfo") {
renderVersionInfo(event.detail.data);
}
});
</script>
</body>
</html>

View File

@@ -226,7 +226,7 @@ inline URL about_version() { return URL::about("version"_string); }
inline bool is_webui_url(URL const& url)
{
return first_is_one_of(url, about_bookmarks(), about_processes(), about_settings());
return first_is_one_of(url, about_bookmarks(), about_processes(), about_settings(), about_version());
}
}

View File

@@ -13,23 +13,9 @@
#include <LibCore/System.h>
#include <LibURL/URL.h>
#include <LibWeb/Loader/GeneratedPagesLoader.h>
#include <LibWeb/Loader/UserAgent.h>
namespace Web {
static String s_browser_process_command_line;
static String s_browser_process_executable_path;
void set_browser_process_command_line(StringView command_line)
{
s_browser_process_command_line = MUST(String::from_utf8(command_line));
}
void set_browser_process_executable_path(StringView executable_path)
{
s_browser_process_executable_path = MUST(String::from_utf8(executable_path));
}
ErrorOr<String> load_error_page(URL::URL const& url, StringView error_message)
{
// Generate HTML error page from error template file
@@ -84,22 +70,4 @@ ErrorOr<String> load_file_directory_page(URL::URL const& url)
return TRY(String::from_utf8(generator.as_string_view()));
}
ErrorOr<String> load_about_version_page()
{
// Generate HTML about version page from template file
// FIXME: Use an actual templating engine (our own one when it's built, preferably with a way to check these usages at compile time)
auto template_file = TRY(Core::Resource::load_from_uri("resource://ladybird/templates/version.html"sv));
StringBuilder builder;
SourceGenerator generator { builder, '%', '%' };
generator.set("browser_name", BROWSER_NAME);
generator.set("browser_version", BROWSER_VERSION);
generator.set("arch_name", CPU_STRING);
generator.set("os_name", OS_STRING);
generator.set("user_agent", default_user_agent);
generator.set("command_line", s_browser_process_command_line);
generator.set("executable_path", s_browser_process_executable_path);
generator.append(template_file->data());
return TRY(String::from_utf8(generator.as_string_view()));
}
}

View File

@@ -11,13 +11,7 @@
namespace Web {
WEB_API void set_browser_process_command_line(StringView command_line);
WEB_API void set_browser_process_executable_path(StringView executable_path);
ErrorOr<String> load_error_page(URL::URL const&, StringView error_message);
ErrorOr<String> load_file_directory_page(URL::URL const&);
ErrorOr<String> load_about_version_page();
}

View File

@@ -287,15 +287,6 @@ void ResourceLoader::handle_about_load_request(LoadRequest const& request, Callb
Requests::RequestTimingInfo timing_info {};
auto serialized_path = URL::percent_decode(url.serialize_path());
// About version page
if (serialized_path == "version") {
auto version_page = MUST(load_about_version_page());
callback(version_page.bytes(), timing_info, response_headers);
return;
}
// Other about static HTML pages
auto target_file = ByteString::formatted("{}.html", serialized_path);
auto about_directory = MUST(Core::Resource::load_from_uri("resource://ladybird/about-pages"_string));

View File

@@ -335,8 +335,6 @@ ErrorOr<void> Application::initialize(Main::Arguments const& arguments)
};
m_web_content_options = {
.command_line = MUST(String::join(' ', m_arguments.strings)),
.executable_path = MUST(String::from_byte_string(MUST(Core::System::current_executable_path()))),
.user_agent_preset = move(user_agent_preset),
.is_test_mode = enable_test_mode ? IsTestMode::Yes : IsTestMode::No,
.log_all_js_exceptions = log_all_js_exceptions ? LogAllJSExceptions::Yes : LogAllJSExceptions::No,

View File

@@ -87,6 +87,8 @@ public:
void open_url_in_new_tab(URL::URL const&, Web::HTML::ActivateTab) const;
void open_bookmark_in_new_tab(String const& bookmark_id, Web::HTML::ActivateTab) const;
Main::Arguments const& command_line_arguments() const { return m_arguments; }
void add_child_process(Process&&);
// FIXME: Should these methods be part of Application, instead of deferring to ProcessManager?

View File

@@ -34,6 +34,7 @@ set(SOURCES
WebUI/BookmarksUI.cpp
WebUI/ProcessesUI.cpp
WebUI/SettingsUI.cpp
WebUI/VersionUI.cpp
)
set(GENERATED_SOURCES ${CURRENT_LIB_GENERATED})

View File

@@ -87,12 +87,7 @@ static ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_proc
auto const& browser_options = WebView::Application::browser_options();
auto const& web_content_options = WebView::Application::web_content_options();
Vector<ByteString> arguments {
"--command-line"sv,
web_content_options.command_line.to_byte_string(),
"--executable-path"sv,
web_content_options.executable_path.to_byte_string(),
};
Vector<ByteString> arguments;
if (browser_options.headless_mode.has_value())
arguments.append("--headless"sv);

View File

@@ -168,8 +168,6 @@ enum class FileSchemeUrlsHaveTupleOrigins {
};
struct WebContentOptions {
String command_line;
String executable_path;
Optional<ByteString> config_path {};
Optional<StringView> user_agent_preset {};
IsTestMode is_test_mode { IsTestMode::No };

View File

@@ -11,6 +11,7 @@
#include <LibWebView/WebUI/BookmarksUI.h>
#include <LibWebView/WebUI/ProcessesUI.h>
#include <LibWebView/WebUI/SettingsUI.h>
#include <LibWebView/WebUI/VersionUI.h>
namespace WebView {
@@ -36,6 +37,8 @@ ErrorOr<RefPtr<WebUI>> WebUI::create(WebContentClient& client, String host)
web_ui = TRY(create_web_ui<ProcessesUI>(client, move(host)));
else if (host == "settings"sv)
web_ui = TRY(create_web_ui<SettingsUI>(client, move(host)));
else if (host == "version"sv)
web_ui = TRY(create_web_ui<VersionUI>(client, move(host)));
if (web_ui)
web_ui->register_interfaces();

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2026, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/JsonObject.h>
#include <AK/String.h>
#include <LibCore/System.h>
#include <LibWeb/Loader/UserAgent.h>
#include <LibWebView/Application.h>
#include <LibWebView/WebUI/VersionUI.h>
namespace WebView {
void VersionUI::register_interfaces()
{
register_interface("loadVersionInfo"sv, [this](auto const&) {
load_version_info();
});
}
void VersionUI::load_version_info()
{
static auto browser_name = String::from_utf8_without_validation({ BROWSER_NAME, __builtin_strlen(BROWSER_NAME) });
static auto browser_version = String::from_utf8_without_validation({ BROWSER_VERSION, __builtin_strlen(BROWSER_VERSION) });
static auto arch = String::from_utf8_without_validation({ CPU_STRING, __builtin_strlen(CPU_STRING) });
static auto platform_name = String::from_utf8_without_validation({ OS_STRING, __builtin_strlen(OS_STRING) });
static auto command_line = MUST(String::join(' ', Application::the().command_line_arguments().strings));
static auto executable_path = MUST(String::from_byte_string(MUST(Core::System::current_executable_path())));
JsonObject version_info;
version_info.set("browserName"_string, browser_name);
version_info.set("browserVersion"_string, browser_version);
version_info.set("arch"_string, arch);
version_info.set("platformName"_string, platform_name);
version_info.set("commandLine"_string, command_line);
version_info.set("executablePath"_string, executable_path);
async_send_message("renderVersionInfo"sv, version_info);
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2026, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWebView/WebUI.h>
namespace WebView {
class VersionUI final : public WebUI {
WEB_UI(VersionUI);
private:
virtual void register_interfaces() override;
void load_version_info();
};
}

View File

@@ -131,8 +131,6 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPlugin);
StringView command_line {};
StringView executable_path {};
auto config_path = ByteString::formatted("{}/ladybird/default-config", WebView::s_ladybird_resource_root);
StringView mach_server_name {};
Vector<ByteString> certificates;
@@ -154,8 +152,6 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
bool file_origins_are_tuple_origins = false;
Core::ArgsParser args_parser;
args_parser.add_option(command_line, "Browser process command line", "command-line", 0, "command_line");
args_parser.add_option(executable_path, "Browser process executable path", "executable-path", 0, "executable_path");
args_parser.add_option(config_path, "Ladybird configuration path", "config-path", 0, "config_path");
args_parser.add_option(enable_test_mode, "Enable test mode", "test-mode");
args_parser.add_option(expose_experimental_interfaces, "Expose experimental IDL interfaces", "expose-experimental-interfaces");
@@ -196,9 +192,6 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
}
font_provider.load_all_fonts_from_uri("resource://fonts"sv);
Web::set_browser_process_command_line(command_line);
Web::set_browser_process_executable_path(executable_path);
// Always use the CPU backend for tests, as the GPU backend is not deterministic
if (force_cpu_painting) {
WebContent::PageClient::set_use_skia_painter(WebContent::PageClient::UseSkiaPainter::CPUBackend);

View File

@@ -74,6 +74,7 @@ set(ABOUT_PAGES
newtab.html
processes.html
settings.html
version.html
webui.css
)
list(TRANSFORM ABOUT_PAGES PREPEND "${LADYBIRD_SOURCE_DIR}/Base/res/ladybird/about-pages/")
@@ -93,7 +94,6 @@ list(TRANSFORM ABOUT_SETTINGS_RESOURCES PREPEND "${LADYBIRD_SOURCE_DIR}/Base/res
set(WEB_TEMPLATES
directory.html
error.html
version.html
)
list(TRANSFORM WEB_TEMPLATES PREPEND "${LADYBIRD_SOURCE_DIR}/Base/res/ladybird/templates/")