LibWeb+LibWebView+WebContent: Convert about:processes to a WebUI

This commit is contained in:
Timothy Flynn 2025-03-24 09:50:12 -04:00
parent bed2ed388c
commit 8a5fb86056
22 changed files with 96 additions and 140 deletions

View file

@ -97,9 +97,9 @@
descending: 2,
});
processes.processes = [];
processes.sortDirection = Direction.ascending;
processes.sortKey = "pid";
window.processes = [];
window.sortDirection = Direction.ascending;
window.sortKey = "pid";
const renderSortedProcesses = () => {
document.querySelectorAll("th").forEach(header => {
@ -107,17 +107,17 @@
header.classList.remove("sorted-descending");
});
if (processes.sortDirection === Direction.ascending) {
document.getElementById(processes.sortKey).classList.add("sorted-ascending");
if (window.sortDirection === Direction.ascending) {
document.getElementById(window.sortKey).classList.add("sorted-ascending");
} else {
document.getElementById(processes.sortKey).classList.add("sorted-descending");
document.getElementById(window.sortKey).classList.add("sorted-descending");
}
const multiplier = processes.sortDirection === Direction.ascending ? 1 : -1;
const multiplier = window.sortDirection === Direction.ascending ? 1 : -1;
processes.processes.sort((lhs, rhs) => {
const lhsValue = lhs[processes.sortKey];
const rhsValue = rhs[processes.sortKey];
window.processes.sort((lhs, rhs) => {
const lhsValue = lhs[window.sortKey];
const rhsValue = rhs[window.sortKey];
if (typeof lhsValue === "string") {
return multiplier * lhsValue.localeCompare(rhsValue);
@ -136,7 +136,7 @@
column.innerText = value;
};
processes.processes.forEach(process => {
window.processes.forEach(process => {
let row = newTable.insertRow();
insertColumn(row, process.name);
insertColumn(row, process.pid);
@ -147,28 +147,34 @@
oldTable.parentNode.replaceChild(newTable, oldTable);
};
processes.loadProcessStatistics = statistics => {
processes.processes = JSON.parse(statistics);
const loadProcessStatistics = processes => {
window.processes = processes;
renderSortedProcesses();
};
document.addEventListener("DOMContentLoaded", () => {
document.addEventListener("WebUILoaded", () => {
document.querySelectorAll("th").forEach(header => {
header.addEventListener("click", () => {
processes.sortDirection = header.classList.contains("sorted-descending")
window.sortDirection = header.classList.contains("sorted-descending")
? Direction.ascending
: Direction.descending;
processes.sortKey = header.getAttribute("id");
window.sortKey = header.getAttribute("id");
renderSortedProcesses();
});
});
setInterval(() => {
processes.updateProcessStatistics();
ladybird.sendMessage("updateProcessStatistics");
}, 1000);
processes.updateProcessStatistics();
ladybird.sendMessage("updateProcessStatistics");
});
document.addEventListener("WebUIMessage", event => {
if (event.detail.name === "loadProcessStatistics") {
loadProcessStatistics(event.detail.data);
}
});
</script>
</body>

View file

@ -563,7 +563,6 @@ set(SOURCES
Internals/InternalAnimationTimeline.cpp
Internals/Internals.cpp
Internals/InternalsBase.cpp
Internals/Processes.cpp
Internals/Settings.cpp
Internals/WebUI.cpp
IntersectionObserver/IntersectionObserver.cpp

View file

@ -624,7 +624,6 @@ class RequestList;
namespace Web::Internals {
class Internals;
class Processes;
class WebUI;
}

View file

@ -60,7 +60,6 @@
#include <LibWeb/HighResolutionTime/TimeOrigin.h>
#include <LibWeb/Infra/CharacterTypes.h>
#include <LibWeb/Internals/Internals.h>
#include <LibWeb/Internals/Processes.h>
#include <LibWeb/Internals/Settings.h>
#include <LibWeb/Layout/Viewport.h>
#include <LibWeb/Page/Page.h>
@ -739,9 +738,7 @@ WebIDL::ExceptionOr<void> Window::initialize_web_interfaces(Badge<WindowEnvironm
if (url.scheme() == "about"sv && url.paths().size() == 1) {
auto const& path = url.paths().first();
if (path == "processes"sv)
define_direct_property("processes"_fly_string, realm.create<Internals::Processes>(realm), JS::default_attributes);
else if (path == "settings"sv)
if (path == "settings"sv)
define_direct_property("settings"_fly_string, realm.create<Internals::Settings>(realm), JS::default_attributes);
}

View file

@ -1,35 +0,0 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/VM.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/ProcessesPrototype.h>
#include <LibWeb/Internals/Processes.h>
#include <LibWeb/Page/Page.h>
namespace Web::Internals {
GC_DEFINE_ALLOCATOR(Processes);
Processes::Processes(JS::Realm& realm)
: InternalsBase(realm)
{
}
Processes::~Processes() = default;
void Processes::initialize(JS::Realm& realm)
{
Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(Processes);
}
void Processes::update_process_statistics()
{
page().client().update_process_statistics();
}
}

View file

@ -1,28 +0,0 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/Internals/InternalsBase.h>
namespace Web::Internals {
class Processes final : public InternalsBase {
WEB_PLATFORM_OBJECT(Processes, InternalsBase);
GC_DECLARE_ALLOCATOR(Processes);
public:
virtual ~Processes() override;
void update_process_statistics();
private:
explicit Processes(JS::Realm&);
virtual void initialize(JS::Realm&) override;
};
}

View file

@ -1,4 +0,0 @@
[Exposed=Nobody]
interface Processes {
undefined updateProcessStatistics();
};

View file

@ -402,8 +402,6 @@ public:
virtual void received_message_from_web_ui([[maybe_unused]] String const& name, [[maybe_unused]] JS::Value data) { }
virtual void update_process_statistics() { }
virtual void request_current_settings() { }
virtual void restore_default_settings() { }
virtual void set_new_tab_page_url(URL::URL const&) { }

View file

@ -266,7 +266,6 @@ libweb_js_bindings(IndexedDB/IDBTransaction)
libweb_js_bindings(IndexedDB/IDBVersionChangeEvent)
libweb_js_bindings(Internals/InternalAnimationTimeline)
libweb_js_bindings(Internals/Internals)
libweb_js_bindings(Internals/Processes)
libweb_js_bindings(Internals/Settings)
libweb_js_bindings(Internals/WebUI)
libweb_js_bindings(IntersectionObserver/IntersectionObserver)

View file

@ -324,19 +324,6 @@ Optional<Process&> Application::find_process(pid_t pid)
return m_process_manager.find_process(pid);
}
void Application::send_updated_process_statistics_to_view(ViewImplementation& view)
{
m_process_manager.update_all_process_statistics();
auto statistics = m_process_manager.serialize_json();
StringBuilder builder;
builder.append("processes.loadProcessStatistics(\""sv);
builder.append_escaped_for_json(statistics);
builder.append("\");"sv);
view.run_javascript(MUST(builder.to_string()));
}
void Application::send_current_settings_to_view(ViewImplementation& view)
{
auto settings = m_settings.serialize_json();

View file

@ -46,6 +46,8 @@ public:
static CookieJar& cookie_jar() { return *the().m_cookie_jar; }
static ProcessManager& process_manager() { return the().m_process_manager; }
Core::EventLoop& event_loop() { return m_event_loop; }
ErrorOr<NonnullRefPtr<WebContentClient>> launch_web_content_process(ViewImplementation&);
@ -59,8 +61,6 @@ public:
#endif
Optional<Process&> find_process(pid_t);
void send_updated_process_statistics_to_view(ViewImplementation&);
void send_current_settings_to_view(ViewImplementation&);
void send_available_search_engines_to_view(ViewImplementation&);

View file

@ -25,6 +25,7 @@ set(SOURCES
ViewImplementation.cpp
WebContentClient.cpp
WebUI.cpp
WebUI/ProcessesUI.cpp
)
if (APPLE)

View file

@ -4,8 +4,8 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/JsonArraySerializer.h>
#include <AK/JsonObjectSerializer.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/String.h>
#include <LibCore/EventLoop.h>
#include <LibCore/System.h>
@ -126,12 +126,10 @@ void ProcessManager::update_all_process_statistics()
(void)update_process_statistics(m_statistics);
}
String ProcessManager::serialize_json()
JsonValue ProcessManager::serialize_json()
{
Threading::MutexLocker locker { m_lock };
StringBuilder builder;
auto serializer = MUST(JsonArraySerializer<>::try_create(builder));
JsonArray serialized;
m_statistics.for_each_process([&](auto const& process) {
auto& process_handle = find_process(process.pid).value();
@ -143,16 +141,15 @@ String ProcessManager::serialize_json()
? MUST(String::formatted("{} - {}", type, *title))
: String::from_utf8_without_validation(type.bytes());
auto object = MUST(serializer.add_object());
MUST(object.add("name"sv, move(process_name)));
MUST(object.add("pid"sv, process.pid));
MUST(object.add("cpu"sv, process.cpu_percent));
MUST(object.add("memory"sv, process.memory_usage_bytes));
MUST(object.finish());
JsonObject object;
object.set("name"sv, move(process_name));
object.set("pid"sv, process.pid);
object.set("cpu"sv, process.cpu_percent);
object.set("memory"sv, process.memory_usage_bytes);
serialized.must_append(move(object));
});
MUST(serializer.finish());
return MUST(builder.to_string());
return serialized;
}
}

View file

@ -6,6 +6,7 @@
#pragma once
#include <AK/JsonValue.h>
#include <AK/Types.h>
#include <LibCore/Platform/ProcessStatistics.h>
#include <LibThreading/Mutex.h>
@ -34,7 +35,7 @@ public:
#endif
void update_all_process_statistics();
String serialize_json();
JsonValue serialize_json();
Function<void(Process&&)> on_process_exited;

View file

@ -677,12 +677,6 @@ Messages::WebContentClient::RequestWorkerAgentResponse WebContentClient::request
return IPC::File {};
}
void WebContentClient::update_process_statistics(u64 page_id)
{
if (auto view = view_for_page_id(page_id); view.has_value())
WebView::Application::the().send_updated_process_statistics_to_view(*view);
}
void WebContentClient::request_current_settings(u64 page_id)
{
if (auto view = view_for_page_id(page_id); view.has_value())

View file

@ -131,7 +131,6 @@ private:
virtual void did_update_navigation_buttons_state(u64 page_id, bool back_enabled, bool forward_enabled) override;
virtual void did_allocate_backing_stores(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap, i32 back_bitmap_id, Gfx::ShareableBitmap) override;
virtual Messages::WebContentClient::RequestWorkerAgentResponse request_worker_agent(u64 page_id) override;
virtual void update_process_statistics(u64 page_id) override;
virtual void request_current_settings(u64 page_id) override;
virtual void restore_default_settings(u64 page_id) override;
virtual void set_new_tab_page_url(u64 page_id, URL::URL new_tab_page_url) override;

View file

@ -8,6 +8,7 @@
#include <LibCore/System.h>
#include <LibWebView/WebContentClient.h>
#include <LibWebView/WebUI.h>
#include <LibWebView/WebUI/ProcessesUI.h>
namespace WebView {
@ -31,10 +32,13 @@ static ErrorOr<NonnullRefPtr<WebUIType>> create_web_ui(WebContentClient& client,
return web_ui;
}
ErrorOr<RefPtr<WebUI>> WebUI::create(WebContentClient&, String)
ErrorOr<RefPtr<WebUI>> WebUI::create(WebContentClient& client, String host)
{
RefPtr<WebUI> web_ui;
if (host == "processes"sv)
web_ui = TRY(create_web_ui<ProcessesUI>(client, move(host)));
if (web_ui)
web_ui->register_interfaces();

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWebView/Application.h>
#include <LibWebView/ProcessManager.h>
#include <LibWebView/WebUI/ProcessesUI.h>
namespace WebView {
void ProcessesUI::register_interfaces()
{
register_interface("updateProcessStatistics"sv, [this](auto const&) {
update_process_statistics();
});
}
void ProcessesUI::update_process_statistics()
{
auto& process_manager = Application::process_manager();
process_manager.update_all_process_statistics();
async_send_message("loadProcessStatistics"sv, process_manager.serialize_json());
}
}

View file

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

View file

@ -704,11 +704,6 @@ void PageClient::page_did_mutate_dom(FlyString const& type, Web::DOM::Node const
client().async_did_mutate_dom(m_id, { type.to_string(), target.unique_id(), move(serialized_target), mutation.release_value() });
}
void PageClient::update_process_statistics()
{
client().async_update_process_statistics(m_id);
}
void PageClient::request_current_settings()
{
client().async_request_current_settings(m_id);

View file

@ -176,7 +176,6 @@ private:
virtual IPC::File request_worker_agent() override;
virtual void page_did_mutate_dom(FlyString const& type, Web::DOM::Node const& target, Web::DOM::NodeList& added_nodes, Web::DOM::NodeList& removed_nodes, GC::Ptr<Web::DOM::Node> previous_sibling, GC::Ptr<Web::DOM::Node> next_sibling, Optional<String> const& attribute_name) override;
virtual void received_message_from_web_ui(String const& name, JS::Value data) override;
virtual void update_process_statistics() override;
virtual void request_current_settings() override;
virtual void restore_default_settings() override;
virtual void set_new_tab_page_url(URL::URL const&) override;

View file

@ -110,8 +110,6 @@ endpoint WebContentClient
request_worker_agent(u64 page_id) => (IPC::File socket) // FIXME: Add required attributes to select a SharedWorker Agent
update_process_statistics(u64 page_id) =|
request_current_settings(u64 page_id) =|
restore_default_settings(u64 page_id) =|
set_new_tab_page_url(u64 page_id, URL::URL new_tab_page_url) =|