LibWebView+LibCore: Manage process lifecycle using a SIGCHLD handler

This large commit also refactors LibWebView's process handling to use
a top-level Application class that uses a new WebView::Process class to
encapsulate the IPC-centric nature of each helper process.
This commit is contained in:
Andrew Kaster 2024-06-29 22:24:01 -06:00 committed by Alexander Kalenik
parent 3dea602d92
commit 4cc3d598f9
Notes: sideshowbarker 2024-07-17 06:51:10 +09:00
24 changed files with 382 additions and 196 deletions

View file

@ -290,6 +290,10 @@
# cmakedefine01 WEBGL_CONTEXT_DEBUG
#endif
#ifndef WEBVIEW_PROCESS_DEBUG
# cmakedefine01 WEBVIEW_PROCESS_DEBUG
#endif
#ifndef WEB_FETCH_DEBUG
# cmakedefine01 WEB_FETCH_DEBUG
#endif

View file

@ -6,7 +6,7 @@
#include <AK/String.h>
#include <LibCore/Timer.h>
#include <LibWebView/ProcessManager.h>
#include <LibWebView/Application.h>
#import <UI/LadybirdWebView.h>
#import <UI/TaskManager.h>
@ -77,8 +77,8 @@ static constexpr CGFloat const WINDOW_HEIGHT = 400;
- (void)updateStatistics
{
WebView::ProcessManager::the().update_all_processes();
[self.web_view loadHTML:WebView::ProcessManager::the().generate_html()];
WebView::Application::the().update_process_statistics();
[self.web_view loadHTML:WebView::Application::the().generate_process_statistics_html()];
}
@end

View file

@ -10,13 +10,12 @@
#include <Ladybird/Types.h>
#include <Ladybird/Utilities.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/EventLoop.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibMain/Main.h>
#include <LibWebView/Application.h>
#include <LibWebView/ChromeProcess.h>
#include <LibWebView/CookieJar.h>
#include <LibWebView/Database.h>
#include <LibWebView/ProcessManager.h>
#include <LibWebView/URL.h>
#include <LibWebView/ViewImplementation.h>
#include <LibWebView/WebContentClient.h>
@ -77,7 +76,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
Application* application = [Application sharedApplication];
Core::EventLoopManager::install(*new Ladybird::CFEventLoopManager);
Core::EventLoop event_loop;
WebView::Application web_view_app(arguments.argc, arguments.argv);
platform_init();
@ -118,16 +117,14 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
open_urls_from_client(raw_urls, NewWindow::Yes);
};
WebView::ProcessManager::initialize();
auto mach_port_server = make<Ladybird::MachPortServer>();
set_mach_server_name(mach_port_server->server_port_name());
mach_port_server->on_receive_child_mach_port = [](auto pid, auto port) {
WebView::ProcessManager::the().add_process(pid, move(port));
mach_port_server->on_receive_child_mach_port = [&web_view_app](auto pid, auto port) {
web_view_app.set_process_mach_port(pid, move(port));
};
mach_port_server->on_receive_backing_stores = [](Ladybird::MachPortServer::BackingStoresMessage message) {
auto view = WebView::WebContentClient::view_for_pid_and_page_id(message.pid, message.page_id);
view->did_allocate_iosurface_backing_stores(message.front_backing_store_id, move(message.front_backing_store_port), message.back_backing_store_id, move(message.back_backing_store_port));
if (auto view = WebView::WebContentClient::view_for_pid_and_page_id(message.pid, message.page_id); view.has_value())
view->did_allocate_iosurface_backing_stores(message.front_backing_store_id, move(message.front_backing_store_port), message.back_backing_store_id, move(message.back_backing_store_port));
};
auto database = TRY(WebView::Database::create());
@ -158,5 +155,5 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
[NSApp setDelegate:delegate];
return event_loop.exec();
return web_view_app.exec();
}

View file

@ -8,7 +8,7 @@
#include "Utilities.h"
#include <AK/Enumerate.h>
#include <LibCore/Process.h>
#include <LibWebView/ProcessManager.h>
#include <LibWebView/Application.h>
template<typename ClientType, typename... ClientArguments>
static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
@ -45,7 +45,7 @@ static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
if constexpr (requires { process.client->set_pid(pid_t {}); })
process.client->set_pid(process.process.pid());
WebView::ProcessManager::the().add_process(WebView::process_type_from_name(server_name), process.process.pid());
WebView::Application::the().add_child_process(WebView::Process { WebView::process_type_from_name(server_name), process.client, move(process.process) });
if (enable_callgrind_profiling == Ladybird::EnableCallgrindProfiling::Yes) {
dbgln();

View file

@ -5,7 +5,7 @@
*/
#include "TaskManagerWindow.h"
#include <LibWebView/ProcessManager.h>
#include <LibWebView/Application.h>
#include <QVBoxLayout>
namespace Ladybird {
@ -41,9 +41,8 @@ void TaskManagerWindow::hideEvent(QHideEvent*)
void TaskManagerWindow::update_statistics()
{
WebView::ProcessManager::the().update_all_processes();
m_web_view->load_html(WebView::ProcessManager::the().generate_html());
WebView::Application::the().update_process_statistics();
m_web_view->load_html(WebView::Application::the().generate_process_statistics_html());
}
}

View file

@ -17,6 +17,7 @@
#include <LibCore/System.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibMain/Main.h>
#include <LibWebView/Application.h>
#include <LibWebView/ChromeProcess.h>
#include <LibWebView/CookieJar.h>
#include <LibWebView/Database.h>
@ -76,8 +77,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
Ladybird::Application app(arguments.argc, arguments.argv);
Core::EventLoopManager::install(*new Ladybird::EventLoopManagerQt);
Core::EventLoop event_loop;
static_cast<Ladybird::EventLoopImplementationQt&>(event_loop.impl()).set_main_loop();
WebView::Application webview_app(arguments.argc, arguments.argv);
static_cast<Ladybird::EventLoopImplementationQt&>(Core::EventLoop::current().impl()).set_main_loop();
TRY(handle_attached_debugger());
@ -142,17 +143,15 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
window.view().load(file_url);
};
WebView::ProcessManager::initialize();
#if defined(AK_OS_MACOS)
auto mach_port_server = make<Ladybird::MachPortServer>();
set_mach_server_name(mach_port_server->server_port_name());
mach_port_server->on_receive_child_mach_port = [](auto pid, auto port) {
WebView::ProcessManager::the().add_process(pid, move(port));
mach_port_server->on_receive_child_mach_port = [&webview_app](auto pid, auto port) {
webview_app.set_process_mach_port(pid, move(port));
};
mach_port_server->on_receive_backing_stores = [](Ladybird::MachPortServer::BackingStoresMessage message) {
auto view = WebView::WebContentClient::view_for_pid_and_page_id(message.pid, message.page_id);
view->did_allocate_iosurface_backing_stores(message.front_backing_store_id, move(message.front_backing_store_port), message.back_backing_store_id, move(message.back_backing_store_port));
if (auto view = WebView::WebContentClient::view_for_pid_and_page_id(message.pid, message.page_id); view.has_value())
view->did_allocate_iosurface_backing_stores(message.front_backing_store_id, move(message.front_backing_store_port), message.back_backing_store_id, move(message.back_backing_store_port));
};
#endif
@ -204,5 +203,5 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
window.show();
return event_loop.exec();
return webview_app.exec();
}

View file

@ -68,6 +68,7 @@ set(WASM_VALIDATOR_DEBUG ON)
set(WEBDRIVER_DEBUG ON)
set(WEBDRIVER_ROUTE_DEBUG ON)
set(WEBGL_CONTEXT_DEBUG ON)
set(WEBVIEW_PROCESS_DEBUG ON)
set(WEB_FETCH_DEBUG ON)
set(WEB_WORKER_DEBUG ON)
set(WEBP_DEBUG ON)

View file

@ -31,6 +31,7 @@ class MimeData;
class NetworkJob;
class NetworkResponse;
class Notifier;
class Process;
class ProcessStatisticsReader;
class Resource;
class ResourceImplementation;

View file

@ -20,8 +20,6 @@ struct ProcessInfo {
{
}
virtual ~ProcessInfo() = default;
pid_t pid { 0 };
u64 memory_usage_bytes { 0 };
@ -30,12 +28,6 @@ struct ProcessInfo {
u64 time_spent_in_process { 0 };
#if defined(AK_OS_MACH)
ProcessInfo(pid_t pid, Core::MachPort&& port)
: pid(pid)
, child_task_port(move(port))
{
}
Core::MachPort child_task_port;
#endif
};

View file

@ -15,11 +15,11 @@
namespace Core::Platform {
struct ProcessStatistics {
template<typename ProcessInfoType, typename Callback>
template<typename Callback>
void for_each_process(Callback&& callback)
{
for (auto& process : processes)
callback(verify_cast<ProcessInfoType>(*process));
callback(*process);
}
u64 total_time_scheduled { 0 };

View file

@ -64,6 +64,13 @@ struct ArgvList {
}
};
Process Process::current()
{
auto p = Process { getpid() };
p.m_should_disown = false;
return p;
}
ErrorOr<Process> Process::spawn(ProcessSpawnOptions const& options)
{
#define CHECK(invocation) \

View file

@ -74,6 +74,7 @@ public:
}
static ErrorOr<Process> spawn(ProcessSpawnOptions const& options);
static Process current();
// FIXME: Make the following 2 functions return Process instance or delete them.
static ErrorOr<pid_t> spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory = {}, KeepAsChild keep_as_child = KeepAsChild::No);

View file

@ -20,9 +20,6 @@ void Client::die()
promise->reject(Error::from_string_literal("ImageDecoder disconnected"));
}
m_pending_decoded_images.clear();
if (on_death)
on_death();
}
NonnullRefPtr<Core::Promise<DecodedImage>> Client::decode_image(ReadonlyBytes encoded_data, Function<ErrorOr<void>(DecodedImage&)> on_resolved, Function<void(Error&)> on_rejected, Optional<Gfx::IntSize> ideal_size, Optional<ByteString> mime_type)

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <LibImageDecoderClient/Client.h>
#include <LibWebView/Application.h>
#include <LibWebView/WebContentClient.h>
namespace WebView {
Application* Application::s_the = nullptr;
Application::Application(int, char**)
{
VERIFY(!s_the);
s_the = this;
m_process_manager.on_process_exited = [this](Process&& process) {
process_did_exit(move(process));
};
}
Application::~Application()
{
s_the = nullptr;
}
int Application::exec()
{
int ret = m_event_loop.exec();
m_in_shutdown = true;
return ret;
}
void Application::add_child_process(WebView::Process&& process)
{
m_process_manager.add_process(move(process));
}
#if defined(AK_OS_MACH)
void Application::set_process_mach_port(pid_t pid, Core::MachPort&& port)
{
m_process_manager.set_process_mach_port(pid, move(port));
}
#endif
Optional<Process&> Application::find_process(pid_t pid)
{
return m_process_manager.find_process(pid);
}
void Application::update_process_statistics()
{
m_process_manager.update_all_process_statistics();
}
String Application::generate_process_statistics_html()
{
return m_process_manager.generate_html();
}
void Application::process_did_exit(Process&& process)
{
if (m_in_shutdown)
return;
dbgln_if(WEBVIEW_PROCESS_DEBUG, "Process {} died, type: {}", process.pid(), process_name_from_type(process.type()));
switch (process.type()) {
case ProcessType::ImageDecoder:
if (auto client = process.client<ImageDecoderClient::Client>(); client.has_value()) {
dbgln_if(WEBVIEW_PROCESS_DEBUG, "Restart ImageDecoder process");
if (auto on_death = move(client->on_death)) {
on_death();
}
}
break;
case ProcessType::RequestServer:
dbgln_if(WEBVIEW_PROCESS_DEBUG, "FIXME: Restart request server");
break;
case ProcessType::WebContent:
if (auto client = process.client<WebContentClient>(); client.has_value()) {
dbgln_if(WEBVIEW_PROCESS_DEBUG, "Restart WebContent process");
if (auto on_web_content_process_crash = move(client->on_web_content_process_crash))
on_web_content_process_crash();
}
break;
case ProcessType::WebWorker:
dbgln_if(WEBVIEW_PROCESS_DEBUG, "WebWorker {} died, not sure what to do.", process.pid());
break;
case ProcessType::Chrome:
dbgln("Invalid process type to be dying: Chrome");
VERIFY_NOT_REACHED();
}
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibCore/EventLoop.h>
#include <LibWebView/Process.h>
#include <LibWebView/ProcessManager.h>
namespace WebView {
class Application {
AK_MAKE_NONCOPYABLE(Application);
public:
Application(int argc, char** argv);
virtual ~Application();
int exec();
static Application& the() { return *s_the; }
Core::EventLoop& event_loop() { return m_event_loop; }
void add_child_process(Process&&);
// FIXME: Should these methods be part of Application, instead of deferring to ProcessManager?
#if defined(AK_OS_MACH)
void set_process_mach_port(pid_t, Core::MachPort&&);
#endif
Optional<Process&> find_process(pid_t);
// FIXME: Should we just expose the ProcessManager via a getter?
void update_process_statistics();
String generate_process_statistics_html();
protected:
virtual void process_did_exit(Process&&);
private:
static Application* s_the;
Core::EventLoop m_event_loop;
ProcessManager m_process_manager;
bool m_in_shutdown { false };
};
}

View file

@ -1,12 +1,14 @@
include(${SerenityOS_SOURCE_DIR}/Meta/CMake/public_suffix.cmake)
set(SOURCES
Application.cpp
Attribute.cpp
ChromeProcess.cpp
CookieJar.cpp
Database.cpp
InspectorClient.cpp
ProcessHandle.cpp
Process.cpp
ProcessManager.cpp
RequestServerAdapter.cpp
SearchEngine.cpp
@ -46,7 +48,7 @@ set(GENERATED_SOURCES
)
serenity_lib(LibWebView webview)
target_link_libraries(LibWebView PRIVATE LibCore LibFileSystem LibGfx LibIPC LibProtocol LibJS LibWeb LibUnicode LibURL)
target_link_libraries(LibWebView PRIVATE LibCore LibFileSystem LibGfx LibImageDecoderClient LibIPC LibProtocol LibJS LibWeb LibUnicode LibURL)
target_compile_definitions(LibWebView PRIVATE ENABLE_PUBLIC_SUFFIX=$<BOOL:${ENABLE_PUBLIC_SUFFIX_DOWNLOAD}>)
# Third-party

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCore/Process.h>
#include <LibWebView/Process.h>
namespace WebView {
Process::Process(ProcessType type, RefPtr<IPC::ConnectionBase> connection, Core::Process process)
: m_process(move(process))
, m_type(type)
, m_connection(move(connection))
{
}
Process::~Process()
{
if (m_connection)
m_connection->shutdown();
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/String.h>
#include <AK/WeakPtr.h>
#include <LibCore/Process.h>
#include <LibIPC/Connection.h>
#include <LibWebView/ProcessType.h>
namespace WebView {
class Process {
AK_MAKE_NONCOPYABLE(Process);
AK_MAKE_DEFAULT_MOVABLE(Process);
public:
Process(ProcessType type, RefPtr<IPC::ConnectionBase> connection, Core::Process process);
~Process();
ProcessType type() const { return m_type; }
Optional<String> const& title() const { return m_title; }
void set_title(Optional<String> title) { m_title = move(title); }
template<typename ConnectionFromClient>
Optional<ConnectionFromClient&> client()
{
if (auto strong_connection = m_connection.strong_ref())
return verify_cast<ConnectionFromClient>(*strong_connection);
return {};
}
pid_t pid() const { return m_process.pid(); }
private:
Core::Process m_process;
ProcessType m_type;
Optional<String> m_title;
WeakPtr<IPC::ConnectionBase> m_connection;
};
}

View file

@ -1,41 +0,0 @@
/*
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/Types.h>
#include <LibCore/Platform/ProcessInfo.h>
#if defined(AK_OS_MACH)
# include <LibCore/MachPort.h>
#endif
namespace WebView {
enum class ProcessType {
Chrome,
WebContent,
WebWorker,
RequestServer,
ImageDecoder,
};
struct ProcessInfo : public Core::Platform::ProcessInfo {
using Core::Platform::ProcessInfo::ProcessInfo;
ProcessInfo(ProcessType type, pid_t pid)
: Core::Platform::ProcessInfo(pid)
, type(type)
{
}
ProcessType type { ProcessType::WebContent };
Optional<String> title;
};
}

View file

@ -12,8 +12,6 @@
namespace WebView {
static sig_atomic_t s_received_sigchld = 0;
ProcessType process_type_from_name(StringView name)
{
if (name == "Chrome"sv)
@ -49,96 +47,74 @@ StringView process_name_from_type(ProcessType type)
}
ProcessManager::ProcessManager()
: on_process_exited([](Process&&) {})
{
}
ProcessManager::~ProcessManager()
{
}
ProcessManager& ProcessManager::the()
{
static ProcessManager s_the;
return s_the;
}
void ProcessManager::initialize()
{
// FIXME: Should we change this to call EventLoop::register_signal?
// Note that only EventLoopImplementationUnix has a working register_signal
struct sigaction action { };
action.sa_flags = SA_RESTART;
action.sa_sigaction = [](int, auto*, auto) {
s_received_sigchld = 1;
};
MUST(Core::System::sigaction(SIGCHLD, &action, nullptr));
the().add_process(WebView::ProcessType::Chrome, getpid());
#ifdef AK_OS_MACH
auto self_send_port = mach_task_self();
auto res = mach_port_mod_refs(mach_task_self(), self_send_port, MACH_PORT_RIGHT_SEND, +1);
VERIFY(res == KERN_SUCCESS);
the().add_process(getpid(), Core::MachPort::adopt_right(self_send_port, Core::MachPort::PortRight::Send));
#endif
}
ProcessInfo* ProcessManager::find_process(pid_t pid)
{
if (auto existing_process = m_statistics.processes.find_if([&](auto& info) { return info->pid == pid; }); !existing_process.is_end())
return verify_cast<ProcessInfo>(existing_process->ptr());
return nullptr;
}
void ProcessManager::add_process(ProcessType type, pid_t pid)
{
Threading::MutexLocker locker { m_lock };
if (auto* existing_process = find_process(pid)) {
existing_process->type = type;
return;
}
m_statistics.processes.append(make<ProcessInfo>(type, pid));
}
#if defined(AK_OS_MACH)
void ProcessManager::add_process(pid_t pid, Core::MachPort&& port)
{
Threading::MutexLocker locker { m_lock };
if (auto* existing_process = find_process(pid)) {
existing_process->child_task_port = move(port);
return;
}
m_statistics.processes.append(make<ProcessInfo>(pid, move(port)));
}
#endif
void ProcessManager::remove_process(pid_t pid)
{
Threading::MutexLocker locker { m_lock };
m_statistics.processes.remove_first_matching([&](auto const& info) {
if (info->pid == pid) {
return true;
}
return false;
});
}
void ProcessManager::update_all_processes()
{
if (s_received_sigchld) {
s_received_sigchld = 0;
m_signal_handle = Core::EventLoop::register_signal(SIGCHLD, [this](int) {
auto result = Core::System::waitpid(-1, WNOHANG);
while (!result.is_error() && result.value().pid > 0) {
auto& [pid, status] = result.value();
if (WIFEXITED(status) || WIFSIGNALED(status)) {
remove_process(pid);
if (auto process = remove_process(pid); process.has_value())
on_process_exited(process.release_value());
}
result = Core::System::waitpid(-1, WNOHANG);
}
}
});
add_process(Process(WebView::ProcessType::Chrome, nullptr, Core::Process::current()));
#ifdef AK_OS_MACH
auto self_send_port = mach_task_self();
auto res = mach_port_mod_refs(mach_task_self(), self_send_port, MACH_PORT_RIGHT_SEND, +1);
VERIFY(res == KERN_SUCCESS);
set_process_mach_port(getpid(), Core::MachPort::adopt_right(self_send_port, Core::MachPort::PortRight::Send));
#endif
}
ProcessManager::~ProcessManager()
{
Core::EventLoop::unregister_signal(m_signal_handle);
}
Optional<Process&> ProcessManager::find_process(pid_t pid)
{
return m_processes.get(pid);
}
void ProcessManager::add_process(WebView::Process&& process)
{
Threading::MutexLocker locker { m_lock };
auto pid = process.pid();
auto result = m_processes.set(pid, move(process));
VERIFY(result == AK::HashSetResult::InsertedNewEntry);
m_statistics.processes.append(make<Core::Platform::ProcessInfo>(pid));
}
#if defined(AK_OS_MACH)
void ProcessManager::set_process_mach_port(pid_t pid, Core::MachPort&& port)
{
Threading::MutexLocker locker { m_lock };
for (auto const& info : m_statistics.processes) {
if (info->pid == pid) {
info->child_task_port = move(port);
return;
}
}
}
#endif
Optional<Process> ProcessManager::remove_process(pid_t pid)
{
Threading::MutexLocker locker { m_lock };
m_statistics.processes.remove_first_matching([&](auto const& info) {
return (info->pid == pid);
});
return m_processes.take(pid);
}
void ProcessManager::update_all_process_statistics()
{
Threading::MutexLocker locker { m_lock };
(void)update_process_statistics(m_statistics);
}
@ -198,12 +174,13 @@ String ProcessManager::generate_html()
<tbody>
)"sv);
m_statistics.for_each_process<ProcessInfo>([&](auto const& process) {
m_statistics.for_each_process([&](auto const& process) {
builder.append("<tr>"sv);
builder.append("<td>"sv);
builder.append(WebView::process_name_from_type(process.type));
if (process.title.has_value())
builder.appendff(" - {}", escape_html_entities(*process.title));
auto& process_handle = this->find_process(process.pid).value();
builder.append(WebView::process_name_from_type(process_handle.type()));
if (process_handle.title().has_value())
builder.appendff(" - {}", escape_html_entities(*process_handle.title()));
builder.append("</td>"sv);
builder.append("<td>"sv);
builder.append(MUST(String::number(process.pid)));

View file

@ -12,7 +12,8 @@
#include <LibCore/Platform/ProcessStatistics.h>
#include <LibThreading/Mutex.h>
#include <LibWebView/Forward.h>
#include <LibWebView/ProcessInfo.h>
#include <LibWebView/Process.h>
#include <LibWebView/ProcessType.h>
namespace WebView {
@ -20,26 +21,29 @@ ProcessType process_type_from_name(StringView);
StringView process_name_from_type(ProcessType type);
class ProcessManager {
AK_MAKE_NONCOPYABLE(ProcessManager);
public:
static ProcessManager& the();
static void initialize();
void add_process(WebView::ProcessType, pid_t);
void remove_process(pid_t);
ProcessInfo* find_process(pid_t);
#if defined(AK_OS_MACH)
void add_process(pid_t, Core::MachPort&&);
#endif
void update_all_processes();
String generate_html();
private:
ProcessManager();
~ProcessManager();
void add_process(Process&&);
Optional<Process> remove_process(pid_t);
Optional<Process&> find_process(pid_t);
#if defined(AK_OS_MACH)
void set_process_mach_port(pid_t, Core::MachPort&&);
#endif
void update_all_process_statistics();
String generate_html();
Function<void(Process&&)> on_process_exited;
private:
Core::Platform::ProcessStatistics m_statistics;
HashMap<pid_t, Process> m_processes;
int m_signal_handle { -1 };
Threading::Mutex m_lock;
};

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
namespace WebView {
enum class ProcessType : u8 {
Chrome,
WebContent,
WebWorker,
RequestServer,
ImageDecoder,
};
}

View file

@ -5,7 +5,7 @@
*/
#include "WebContentClient.h"
#include "ProcessManager.h"
#include "Application.h"
#include "ViewImplementation.h"
#include <LibWeb/Cookie/ParsedCookie.h>
@ -36,8 +36,7 @@ WebContentClient::~WebContentClient()
void WebContentClient::die()
{
VERIFY(on_web_content_process_crash);
on_web_content_process_crash();
// Intentionally empty. Restart is handled at another level.
}
void WebContentClient::register_view(u64 page_id, ViewImplementation& view)
@ -49,6 +48,9 @@ void WebContentClient::register_view(u64 page_id, ViewImplementation& view)
void WebContentClient::unregister_view(u64 page_id)
{
m_views.remove(page_id);
if (m_views.is_empty()) {
on_web_content_process_crash = nullptr;
}
}
void WebContentClient::did_paint(u64 page_id, Gfx::IntRect const& rect, i32 bitmap_id)
@ -59,8 +61,8 @@ void WebContentClient::did_paint(u64 page_id, Gfx::IntRect const& rect, i32 bitm
void WebContentClient::did_start_loading(u64 page_id, URL::URL const& url, bool is_redirect)
{
if (auto* process = WebView::ProcessManager::the().find_process(m_process_handle.pid))
process->title.clear();
if (auto process = WebView::Application::the().find_process(m_process_handle.pid); process.has_value())
process->set_title(OptionalNone {});
if (auto view = view_for_page_id(page_id); view.has_value()) {
view->set_url({}, url);
@ -143,8 +145,8 @@ void WebContentClient::did_layout(u64 page_id, Gfx::IntSize content_size)
void WebContentClient::did_change_title(u64 page_id, ByteString const& title)
{
if (auto* process = WebView::ProcessManager::the().find_process(m_process_handle.pid))
process->title = MUST(String::from_byte_string(title));
if (auto process = WebView::Application::the().find_process(m_process_handle.pid); process.has_value())
process->set_title(MUST(String::from_byte_string(title)));
if (auto view = view_for_page_id(page_id); view.has_value()) {
if (!view->on_title_change)

View file

@ -51,6 +51,7 @@
#include <LibWeb/HTML/ActivateTab.h>
#include <LibWeb/HTML/SelectedFile.h>
#include <LibWeb/Worker/WebWorkerClient.h>
#include <LibWebView/Application.h>
#include <LibWebView/CookieJar.h>
#include <LibWebView/Database.h>
#include <LibWebView/URL.h>
@ -631,7 +632,7 @@ static ErrorOr<int> run_tests(HeadlessWebContentView& view, StringView test_root
ErrorOr<int> serenity_main(Main::Arguments arguments)
{
Core::EventLoop event_loop;
WebView::Application app(arguments.argc, arguments.argv);
int screenshot_timeout = 1;
StringView raw_url;
@ -704,8 +705,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
}
if (web_driver_ipc_path.is_empty()) {
auto timer = TRY(load_page_for_screenshot_and_exit(event_loop, *view, url.value(), screenshot_timeout));
return event_loop.exec();
auto timer = TRY(load_page_for_screenshot_and_exit(Core::EventLoop::current(), *view, url.value(), screenshot_timeout));
return app.exec();
}
return 0;