mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-18 15:32:22 +00:00
LibWebView+Services+UI: Move process helpers to LibWebView
This commit is contained in:
parent
a14937c45e
commit
0ff91a5273
Notes:
github-actions[bot]
2024-11-11 12:36:39 +00:00
Author: https://github.com/trflynn89
Commit: 0ff91a5273
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2269
29 changed files with 110 additions and 119 deletions
|
@ -7,6 +7,7 @@ set(SOURCES
|
|||
ChromeProcess.cpp
|
||||
CookieJar.cpp
|
||||
Database.cpp
|
||||
HelperProcess.cpp
|
||||
InspectorClient.cpp
|
||||
Plugins/FontPlugin.cpp
|
||||
Plugins/ImageCodecPlugin.cpp
|
||||
|
@ -17,11 +18,16 @@ set(SOURCES
|
|||
SourceHighlighter.cpp
|
||||
URL.cpp
|
||||
UserAgent.cpp
|
||||
Utilities.cpp
|
||||
ViewImplementation.cpp
|
||||
WebContentClient.cpp
|
||||
${PUBLIC_SUFFIX_SOURCES}
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND SOURCES MachPortServer.cpp)
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
list(APPEND SOURCES
|
||||
EventLoop/EventLoopImplementationQt.cpp
|
||||
|
@ -49,6 +55,10 @@ embed_as_string(
|
|||
compile_ipc(UIProcessServer.ipc UIProcessServerEndpoint.h)
|
||||
compile_ipc(UIProcessClient.ipc UIProcessClientEndpoint.h)
|
||||
|
||||
if (NOT APPLE AND NOT CMAKE_INSTALL_LIBEXECDIR STREQUAL "libexec")
|
||||
set_source_files_properties(Utilities.cpp PROPERTIES COMPILE_DEFINITIONS LADYBIRD_LIBEXECDIR="${CMAKE_INSTALL_LIBEXECDIR}")
|
||||
endif()
|
||||
|
||||
set(GENERATED_SOURCES
|
||||
${GENERATED_SOURCES}
|
||||
../../Services/RequestServer/RequestClientEndpoint.h
|
||||
|
@ -66,6 +76,10 @@ serenity_lib(LibWebView webview)
|
|||
target_link_libraries(LibWebView PRIVATE LibCore LibFileSystem LibGfx LibImageDecoderClient LibIPC LibRequests LibJS LibWeb LibUnicode LibURL LibSyntax)
|
||||
target_compile_definitions(LibWebView PRIVATE ENABLE_PUBLIC_SUFFIX=$<BOOL:${ENABLE_PUBLIC_SUFFIX_DOWNLOAD}>)
|
||||
|
||||
if (APPLE)
|
||||
target_link_libraries(LibWebView PRIVATE LibThreading)
|
||||
endif()
|
||||
|
||||
# Third-party
|
||||
find_package(SQLite3 REQUIRED)
|
||||
target_link_libraries(LibWebView PRIVATE SQLite::SQLite3)
|
||||
|
|
197
Libraries/LibWebView/HelperProcess.cpp
Normal file
197
Libraries/LibWebView/HelperProcess.cpp
Normal file
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Enumerate.h>
|
||||
#include <LibCore/Process.h>
|
||||
#include <LibWebView/Application.h>
|
||||
#include <LibWebView/HelperProcess.h>
|
||||
#include <LibWebView/Utilities.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
template<typename ClientType, typename... ClientArguments>
|
||||
static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
|
||||
StringView server_name,
|
||||
ReadonlySpan<ByteString> candidate_server_paths,
|
||||
Vector<ByteString> arguments,
|
||||
ClientArguments&&... client_arguments)
|
||||
{
|
||||
auto process_type = WebView::process_type_from_name(server_name);
|
||||
auto const& chrome_options = WebView::Application::chrome_options();
|
||||
|
||||
if (chrome_options.profile_helper_process == process_type) {
|
||||
arguments.prepend({
|
||||
"--tool=callgrind"sv,
|
||||
"--instr-atstart=no"sv,
|
||||
""sv, // Placeholder for the process path.
|
||||
});
|
||||
}
|
||||
|
||||
if (chrome_options.debug_helper_process == process_type)
|
||||
arguments.append("--wait-for-debugger"sv);
|
||||
|
||||
for (auto [i, path] : enumerate(candidate_server_paths)) {
|
||||
Core::ProcessSpawnOptions options { .name = server_name, .arguments = arguments };
|
||||
|
||||
if (chrome_options.profile_helper_process == process_type) {
|
||||
options.executable = "valgrind"sv;
|
||||
options.search_for_executable_in_path = true;
|
||||
arguments[2] = path;
|
||||
} else {
|
||||
options.executable = path;
|
||||
}
|
||||
|
||||
auto result = WebView::Process::spawn<ClientType>(process_type, move(options), forward<ClientArguments>(client_arguments)...);
|
||||
|
||||
if (!result.is_error()) {
|
||||
auto&& [process, client] = result.release_value();
|
||||
|
||||
if constexpr (requires { client->set_pid(pid_t {}); })
|
||||
client->set_pid(process.pid());
|
||||
|
||||
WebView::Application::the().add_child_process(move(process));
|
||||
|
||||
if (chrome_options.profile_helper_process == process_type) {
|
||||
dbgln();
|
||||
dbgln("\033[1;45mLaunched {} process under callgrind!\033[0m", server_name);
|
||||
dbgln("\033[100mRun `\033[4mcallgrind_control -i on\033[24m` to start instrumentation and `\033[4mcallgrind_control -i off\033[24m` stop it again.\033[0m");
|
||||
dbgln();
|
||||
}
|
||||
|
||||
return move(client);
|
||||
}
|
||||
|
||||
if (i == candidate_server_paths.size() - 1) {
|
||||
warnln("Could not launch any of {}: {}", candidate_server_paths, result.error());
|
||||
return result.release_error();
|
||||
}
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
|
||||
WebView::ViewImplementation& view,
|
||||
ReadonlySpan<ByteString> candidate_web_content_paths,
|
||||
IPC::File image_decoder_socket,
|
||||
Optional<IPC::File> request_server_socket)
|
||||
{
|
||||
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(),
|
||||
};
|
||||
|
||||
if (web_content_options.config_path.has_value()) {
|
||||
arguments.append("--config-path"sv);
|
||||
arguments.append(web_content_options.config_path.value());
|
||||
}
|
||||
if (web_content_options.is_layout_test_mode == WebView::IsLayoutTestMode::Yes)
|
||||
arguments.append("--layout-test-mode"sv);
|
||||
if (web_content_options.log_all_js_exceptions == WebView::LogAllJSExceptions::Yes)
|
||||
arguments.append("--log-all-js-exceptions"sv);
|
||||
if (web_content_options.enable_idl_tracing == WebView::EnableIDLTracing::Yes)
|
||||
arguments.append("--enable-idl-tracing"sv);
|
||||
if (web_content_options.enable_http_cache == WebView::EnableHTTPCache::Yes)
|
||||
arguments.append("--enable-http-cache"sv);
|
||||
if (web_content_options.expose_internals_object == WebView::ExposeInternalsObject::Yes)
|
||||
arguments.append("--expose-internals-object"sv);
|
||||
if (web_content_options.force_cpu_painting == WebView::ForceCPUPainting::Yes)
|
||||
arguments.append("--force-cpu-painting"sv);
|
||||
if (web_content_options.force_fontconfig == WebView::ForceFontconfig::Yes)
|
||||
arguments.append("--force-fontconfig"sv);
|
||||
if (web_content_options.collect_garbage_on_every_allocation == WebView::CollectGarbageOnEveryAllocation::Yes)
|
||||
arguments.append("--collect-garbage-on-every-allocation"sv);
|
||||
|
||||
if (auto server = mach_server_name(); server.has_value()) {
|
||||
arguments.append("--mach-server-name"sv);
|
||||
arguments.append(server.value());
|
||||
}
|
||||
if (request_server_socket.has_value()) {
|
||||
arguments.append("--request-server-socket"sv);
|
||||
arguments.append(ByteString::number(request_server_socket->fd()));
|
||||
}
|
||||
|
||||
arguments.append("--image-decoder-socket"sv);
|
||||
arguments.append(ByteString::number(image_decoder_socket.fd()));
|
||||
|
||||
return launch_server_process<WebView::WebContentClient>("WebContent"sv, candidate_web_content_paths, move(arguments), view);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_image_decoder_process(ReadonlySpan<ByteString> candidate_image_decoder_paths)
|
||||
{
|
||||
Vector<ByteString> arguments;
|
||||
if (auto server = mach_server_name(); server.has_value()) {
|
||||
arguments.append("--mach-server-name"sv);
|
||||
arguments.append(server.value());
|
||||
}
|
||||
|
||||
return launch_server_process<ImageDecoderClient::Client>("ImageDecoder"sv, candidate_image_decoder_paths, arguments);
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Web::HTML::WebWorkerClient>> launch_web_worker_process(ReadonlySpan<ByteString> candidate_web_worker_paths, NonnullRefPtr<Requests::RequestClient> request_client)
|
||||
{
|
||||
Vector<ByteString> arguments;
|
||||
|
||||
auto socket = TRY(connect_new_request_server_client(*request_client));
|
||||
arguments.append("--request-server-socket"sv);
|
||||
arguments.append(ByteString::number(socket.fd()));
|
||||
|
||||
return launch_server_process<Web::HTML::WebWorkerClient>("WebWorker"sv, candidate_web_worker_paths, move(arguments));
|
||||
}
|
||||
|
||||
ErrorOr<NonnullRefPtr<Requests::RequestClient>> launch_request_server_process(ReadonlySpan<ByteString> candidate_request_server_paths, StringView serenity_resource_root)
|
||||
{
|
||||
Vector<ByteString> arguments;
|
||||
|
||||
if (!serenity_resource_root.is_empty()) {
|
||||
arguments.append("--serenity-resource-root"sv);
|
||||
arguments.append(serenity_resource_root);
|
||||
}
|
||||
|
||||
for (auto const& certificate : WebView::Application::chrome_options().certificates)
|
||||
arguments.append(ByteString::formatted("--certificate={}", certificate));
|
||||
|
||||
if (auto server = mach_server_name(); server.has_value()) {
|
||||
arguments.append("--mach-server-name"sv);
|
||||
arguments.append(server.value());
|
||||
}
|
||||
|
||||
return launch_server_process<Requests::RequestClient>("RequestServer"sv, candidate_request_server_paths, move(arguments));
|
||||
}
|
||||
|
||||
ErrorOr<IPC::File> connect_new_request_server_client(Requests::RequestClient& client)
|
||||
{
|
||||
auto new_socket = client.send_sync_but_allow_failure<Messages::RequestServer::ConnectNewClient>();
|
||||
if (!new_socket)
|
||||
return Error::from_string_literal("Failed to connect to RequestServer");
|
||||
|
||||
auto socket = new_socket->take_client_socket();
|
||||
TRY(socket.clear_close_on_exec());
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
ErrorOr<IPC::File> connect_new_image_decoder_client(ImageDecoderClient::Client& client)
|
||||
{
|
||||
auto new_socket = client.send_sync_but_allow_failure<Messages::ImageDecoderServer::ConnectNewClients>(1);
|
||||
if (!new_socket)
|
||||
return Error::from_string_literal("Failed to connect to ImageDecoder");
|
||||
|
||||
auto sockets = new_socket->take_sockets();
|
||||
if (sockets.size() != 1)
|
||||
return Error::from_string_literal("Failed to connect to ImageDecoder");
|
||||
|
||||
auto socket = sockets.take_last();
|
||||
TRY(socket.clear_close_on_exec());
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
}
|
34
Libraries/LibWebView/HelperProcess.h
Normal file
34
Libraries/LibWebView/HelperProcess.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Span.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <LibImageDecoderClient/Client.h>
|
||||
#include <LibRequests/RequestClient.h>
|
||||
#include <LibWeb/Worker/WebWorkerClient.h>
|
||||
#include <LibWebView/ViewImplementation.h>
|
||||
#include <LibWebView/WebContentClient.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_process(
|
||||
WebView::ViewImplementation& view,
|
||||
ReadonlySpan<ByteString> candidate_web_content_paths,
|
||||
IPC::File image_decoder_socket,
|
||||
Optional<IPC::File> request_server_socket = {});
|
||||
|
||||
ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_image_decoder_process(ReadonlySpan<ByteString> candidate_image_decoder_paths);
|
||||
ErrorOr<NonnullRefPtr<Web::HTML::WebWorkerClient>> launch_web_worker_process(ReadonlySpan<ByteString> candidate_web_worker_paths, NonnullRefPtr<Requests::RequestClient>);
|
||||
ErrorOr<NonnullRefPtr<Requests::RequestClient>> launch_request_server_process(ReadonlySpan<ByteString> candidate_request_server_paths, StringView serenity_resource_root);
|
||||
|
||||
ErrorOr<IPC::File> connect_new_request_server_client(Requests::RequestClient&);
|
||||
ErrorOr<IPC::File> connect_new_image_decoder_client(ImageDecoderClient::Client&);
|
||||
|
||||
}
|
105
Libraries/LibWebView/MachPortServer.cpp
Normal file
105
Libraries/LibWebView/MachPortServer.cpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Debug.h>
|
||||
#include <LibCore/Platform/MachMessageTypes.h>
|
||||
#include <LibCore/Platform/ProcessStatisticsMach.h>
|
||||
#include <LibWebView/MachPortServer.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
MachPortServer::MachPortServer()
|
||||
: m_thread(Threading::Thread::construct([this]() -> intptr_t { thread_loop(); return 0; }, "MachPortServer"sv))
|
||||
, m_server_port_name(ByteString::formatted("org.ladybird.Ladybird.helper.{}", getpid()))
|
||||
{
|
||||
if (auto err = allocate_server_port(); err.is_error())
|
||||
dbgln("Failed to allocate server port: {}", err.error());
|
||||
else
|
||||
start();
|
||||
}
|
||||
|
||||
MachPortServer::~MachPortServer()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void MachPortServer::start()
|
||||
{
|
||||
m_thread->start();
|
||||
}
|
||||
|
||||
void MachPortServer::stop()
|
||||
{
|
||||
// FIXME: We should join instead (after storing should_stop = false) when we have a way to interrupt the thread's mach_msg call
|
||||
m_thread->detach();
|
||||
m_should_stop.store(true, MemoryOrder::memory_order_release);
|
||||
}
|
||||
|
||||
bool MachPortServer::is_initialized()
|
||||
{
|
||||
return MACH_PORT_VALID(m_server_port_recv_right.port()) && MACH_PORT_VALID(m_server_port_send_right.port());
|
||||
}
|
||||
|
||||
ErrorOr<void> MachPortServer::allocate_server_port()
|
||||
{
|
||||
m_server_port_recv_right = TRY(Core::MachPort::create_with_right(Core::MachPort::PortRight::Receive));
|
||||
m_server_port_send_right = TRY(m_server_port_recv_right.insert_right(Core::MachPort::MessageRight::MakeSend));
|
||||
TRY(m_server_port_recv_right.register_with_bootstrap_server(m_server_port_name));
|
||||
|
||||
dbgln_if(MACH_PORT_DEBUG, "Success! we created and attached mach port {:x} to bootstrap server with name {}", m_server_port_recv_right.port(), m_server_port_name);
|
||||
return {};
|
||||
}
|
||||
|
||||
void MachPortServer::thread_loop()
|
||||
{
|
||||
while (!m_should_stop.load(MemoryOrder::memory_order_acquire)) {
|
||||
Core::Platform::ReceivedMachMessage message {};
|
||||
|
||||
// Get the pid of the child from the audit trailer so we can associate the port w/it
|
||||
mach_msg_options_t const options = MACH_RCV_MSG | MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
|
||||
|
||||
// FIXME: How can we interrupt this call during application shutdown?
|
||||
auto const ret = mach_msg(&message.header, options, 0, sizeof(message), m_server_port_recv_right.port(), MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
if (ret != KERN_SUCCESS) {
|
||||
dbgln("mach_msg failed: {}", mach_error_string(ret));
|
||||
break;
|
||||
}
|
||||
|
||||
if (message.header.msgh_id == Core::Platform::BACKING_STORE_IOSURFACES_MESSAGE_ID) {
|
||||
auto pid = static_cast<pid_t>(message.body.parent_iosurface.trailer.msgh_audit.val[5]);
|
||||
auto const& backing_stores_message = message.body.parent_iosurface;
|
||||
auto front_child_port = Core::MachPort::adopt_right(backing_stores_message.front_descriptor.name, Core::MachPort::PortRight::Send);
|
||||
auto back_child_port = Core::MachPort::adopt_right(backing_stores_message.back_descriptor.name, Core::MachPort::PortRight::Send);
|
||||
auto const& metadata = backing_stores_message.metadata;
|
||||
if (on_receive_backing_stores)
|
||||
on_receive_backing_stores({ .pid = pid,
|
||||
.page_id = metadata.page_id,
|
||||
.front_backing_store_id = metadata.front_backing_store_id,
|
||||
.back_backing_store_id = metadata.back_backing_store_id,
|
||||
.front_backing_store_port = move(front_child_port),
|
||||
.back_backing_store_port = move(back_child_port) });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (message.header.msgh_id == Core::Platform::SELF_TASK_PORT_MESSAGE_ID) {
|
||||
if (MACH_MSGH_BITS_LOCAL(message.header.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND) {
|
||||
dbgln("Received message with invalid local port rights {}, ignoring", MACH_MSGH_BITS_LOCAL(message.header.msgh_bits));
|
||||
continue;
|
||||
}
|
||||
|
||||
auto const& task_port_message = message.body.parent;
|
||||
auto pid = static_cast<pid_t>(task_port_message.trailer.msgh_audit.val[5]);
|
||||
auto child_port = Core::MachPort::adopt_right(task_port_message.port_descriptor.name, Core::MachPort::PortRight::Send);
|
||||
dbgln_if(MACH_PORT_DEBUG, "Received child port {:x} from pid {}", child_port.port(), pid);
|
||||
if (on_receive_child_mach_port)
|
||||
on_receive_child_mach_port(pid, move(child_port));
|
||||
continue;
|
||||
}
|
||||
|
||||
dbgln("Received message with id {}, ignoring", message.header.msgh_id);
|
||||
}
|
||||
}
|
||||
}
|
57
Libraries/LibWebView/MachPortServer.h
Normal file
57
Libraries/LibWebView/MachPortServer.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Atomic.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibCore/MachPort.h>
|
||||
#include <LibThreading/Thread.h>
|
||||
|
||||
#if !defined(AK_OS_MACH)
|
||||
# error "This file is only for Mach kernel-based OS's"
|
||||
#endif
|
||||
|
||||
namespace WebView {
|
||||
|
||||
class MachPortServer {
|
||||
|
||||
public:
|
||||
MachPortServer();
|
||||
~MachPortServer();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
bool is_initialized();
|
||||
|
||||
Function<void(pid_t, Core::MachPort)> on_receive_child_mach_port;
|
||||
struct BackingStoresMessage {
|
||||
pid_t pid { -1 };
|
||||
u64 page_id { 0 };
|
||||
i32 front_backing_store_id { 0 };
|
||||
i32 back_backing_store_id { 0 };
|
||||
Core::MachPort front_backing_store_port;
|
||||
Core::MachPort back_backing_store_port;
|
||||
};
|
||||
Function<void(BackingStoresMessage)> on_receive_backing_stores;
|
||||
|
||||
ByteString const& server_port_name() const { return m_server_port_name; }
|
||||
|
||||
private:
|
||||
void thread_loop();
|
||||
ErrorOr<void> allocate_server_port();
|
||||
|
||||
NonnullRefPtr<Threading::Thread> m_thread;
|
||||
ByteString const m_server_port_name;
|
||||
Core::MachPort m_server_port_recv_right;
|
||||
Core::MachPort m_server_port_send_right;
|
||||
|
||||
Atomic<bool> m_should_stop { false };
|
||||
};
|
||||
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
#include <LibImageDecoderClient/Client.h>
|
||||
#include <LibWebView/Plugins/ImageCodecPlugin.h>
|
||||
#include <UI/Utilities.h>
|
||||
#include <LibWebView/Utilities.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
|
|
118
Libraries/LibWebView/Utilities.cpp
Normal file
118
Libraries/LibWebView/Utilities.cpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <LibCore/Directory.h>
|
||||
#include <LibCore/Environment.h>
|
||||
#include <LibCore/Resource.h>
|
||||
#include <LibCore/ResourceImplementationFile.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <LibWebView/Utilities.h>
|
||||
|
||||
#define TOKENCAT(x, y) x##y
|
||||
#define STRINGIFY(x) TOKENCAT(x, sv)
|
||||
|
||||
namespace WebView {
|
||||
|
||||
// This is expected to be set from the build scripts, if a packager desires
|
||||
#if defined(LADYBIRD_LIBEXECDIR)
|
||||
static constexpr auto libexec_path = STRINGIFY(LADYBIRD_LIBEXECDIR);
|
||||
#else
|
||||
static constexpr auto libexec_path = "libexec"sv;
|
||||
#endif
|
||||
|
||||
ByteString s_ladybird_resource_root;
|
||||
|
||||
Optional<ByteString> s_mach_server_name;
|
||||
|
||||
Optional<ByteString const&> mach_server_name()
|
||||
{
|
||||
if (s_mach_server_name.has_value())
|
||||
return *s_mach_server_name;
|
||||
return {};
|
||||
}
|
||||
|
||||
void set_mach_server_name(ByteString name)
|
||||
{
|
||||
s_mach_server_name = move(name);
|
||||
}
|
||||
|
||||
ErrorOr<ByteString> application_directory()
|
||||
{
|
||||
auto current_executable_path = TRY(Core::System::current_executable_path());
|
||||
return LexicalPath::dirname(current_executable_path);
|
||||
}
|
||||
|
||||
[[gnu::used]] static LexicalPath find_prefix(LexicalPath const& application_directory);
|
||||
static LexicalPath find_prefix(LexicalPath const& application_directory)
|
||||
{
|
||||
if (application_directory.string().ends_with(libexec_path)) {
|
||||
// Strip libexec_path if it's there
|
||||
return LexicalPath(application_directory.string().substring_view(0, application_directory.string().length() - libexec_path.length()));
|
||||
}
|
||||
|
||||
// Otherwise, we are in $prefix/bin
|
||||
return application_directory.parent();
|
||||
}
|
||||
|
||||
void platform_init()
|
||||
{
|
||||
s_ladybird_resource_root = [] {
|
||||
auto home = Core::Environment::get("XDG_CONFIG_HOME"sv)
|
||||
.value_or_lazy_evaluated_optional([]() { return Core::Environment::get("HOME"sv); });
|
||||
if (home.has_value()) {
|
||||
auto home_lagom = ByteString::formatted("{}/.lagom", home);
|
||||
if (FileSystem::is_directory(home_lagom))
|
||||
return home_lagom;
|
||||
}
|
||||
auto app_dir = MUST(application_directory());
|
||||
#ifdef AK_OS_MACOS
|
||||
return LexicalPath(app_dir).parent().append("Resources"sv).string();
|
||||
#else
|
||||
return find_prefix(LexicalPath(app_dir)).append("share/Lagom"sv).string();
|
||||
#endif
|
||||
}();
|
||||
Core::ResourceImplementation::install(make<Core::ResourceImplementationFile>(MUST(String::from_byte_string(s_ladybird_resource_root))));
|
||||
}
|
||||
|
||||
void copy_default_config_files(StringView config_path)
|
||||
{
|
||||
MUST(Core::Directory::create(config_path, Core::Directory::CreateDirectories::Yes));
|
||||
|
||||
auto config_resources = MUST(Core::Resource::load_from_uri("resource://ladybird/default-config"sv));
|
||||
|
||||
config_resources->for_each_descendant_file([config_path](Core::Resource const& resource) -> IterationDecision {
|
||||
auto file_path = ByteString::formatted("{}/{}", config_path, resource.filename());
|
||||
|
||||
if (Core::System::stat(file_path).is_error()) {
|
||||
auto file = MUST(Core::File::open(file_path, Core::File::OpenMode::Write));
|
||||
MUST(file->write_until_depleted(resource.data()));
|
||||
}
|
||||
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<Vector<ByteString>> get_paths_for_helper_process(StringView process_name)
|
||||
{
|
||||
auto application_path = TRY(application_directory());
|
||||
Vector<ByteString> paths;
|
||||
|
||||
#if !defined(AK_OS_MACOS)
|
||||
auto prefix = find_prefix(LexicalPath(application_path));
|
||||
TRY(paths.try_append(LexicalPath::join(prefix.string(), libexec_path, process_name).string()));
|
||||
TRY(paths.try_append(LexicalPath::join(prefix.string(), "bin"sv, process_name).string()));
|
||||
#endif
|
||||
TRY(paths.try_append(ByteString::formatted("{}/{}", application_path, process_name)));
|
||||
TRY(paths.try_append(ByteString::formatted("./{}", process_name)));
|
||||
// NOTE: Add platform-specific paths here
|
||||
return paths;
|
||||
}
|
||||
|
||||
}
|
26
Libraries/LibWebView/Utilities.h
Normal file
26
Libraries/LibWebView/Utilities.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
void platform_init();
|
||||
void copy_default_config_files(StringView config_path);
|
||||
ErrorOr<ByteString> application_directory();
|
||||
ErrorOr<Vector<ByteString>> get_paths_for_helper_process(StringView process_name);
|
||||
|
||||
extern ByteString s_ladybird_resource_root;
|
||||
Optional<ByteString const&> mach_server_name();
|
||||
void set_mach_server_name(ByteString name);
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue