mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
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:
Notes:
github-actions[bot]
2026-04-21 11:00:17 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/06796f5f7fa Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/9001
@@ -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>
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -34,6 +34,7 @@ set(SOURCES
|
||||
WebUI/BookmarksUI.cpp
|
||||
WebUI/ProcessesUI.cpp
|
||||
WebUI/SettingsUI.cpp
|
||||
WebUI/VersionUI.cpp
|
||||
)
|
||||
|
||||
set(GENERATED_SOURCES ${CURRENT_LIB_GENERATED})
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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();
|
||||
|
||||
43
Libraries/LibWebView/WebUI/VersionUI.cpp
Normal file
43
Libraries/LibWebView/WebUI/VersionUI.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
22
Libraries/LibWebView/WebUI/VersionUI.h
Normal file
22
Libraries/LibWebView/WebUI/VersionUI.h
Normal 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();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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/")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user