mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 23:20:20 +00:00
LibWebView: Create plumbing for a single UI process
This allows main UI processes created while there is a currently running one to request a new tab or a new window with the initial urls provided on the command line. This matches (almost) the behavior of Chromium and Firefox. Add a new IPC protocol between two UI processes. The main UI process will create an IPC server socket, while secondary UI processes will connect to that socket and send over the URLs and action it wants the main process to take.
This commit is contained in:
parent
12a9702acb
commit
bc976fe7e1
Notes:
sideshowbarker
2024-07-17 01:06:10 +09:00
Author: https://github.com/ADKaster Commit: https://github.com/SerenityOS/serenity/commit/bc976fe7e1 Pull-request: https://github.com/SerenityOS/serenity/pull/24126 Reviewed-by: https://github.com/trflynn89 ✅
6 changed files with 231 additions and 0 deletions
|
@ -84,6 +84,28 @@ compiled_action("WebDriverServerEndpoint") {
|
|||
]
|
||||
}
|
||||
|
||||
compiled_action("UIProcessClientEndpoint") {
|
||||
tool = "//Meta/Lagom/Tools/CodeGenerators/IPCCompiler"
|
||||
inputs = [ "//Userland/Libraries/LibWebView/UIProcessClient.ipc" ]
|
||||
outputs = [ "$root_gen_dir/LibWebView/UIProcessClientEndpoint.h" ]
|
||||
args = [
|
||||
rebase_path(inputs[0], root_build_dir),
|
||||
"-o",
|
||||
rebase_path(outputs[0], root_build_dir),
|
||||
]
|
||||
}
|
||||
|
||||
compiled_action("UIProcessServerEndpoint") {
|
||||
tool = "//Meta/Lagom/Tools/CodeGenerators/IPCCompiler"
|
||||
inputs = [ "//Userland/Libraries/LibWebView/UIProcessServer.ipc" ]
|
||||
outputs = [ "$root_gen_dir/LibWebView/UIProcessServerEndpoint.h" ]
|
||||
args = [
|
||||
rebase_path(inputs[0], root_build_dir),
|
||||
"-o",
|
||||
rebase_path(outputs[0], root_build_dir),
|
||||
]
|
||||
}
|
||||
|
||||
embed_as_string_view("generate_native_stylesheet_source") {
|
||||
input = "Native.css"
|
||||
output = "$target_gen_dir/NativeStyleSheetSource.cpp"
|
||||
|
@ -100,6 +122,8 @@ shared_library("LibWebView") {
|
|||
"$target_gen_dir/..",
|
||||
]
|
||||
deps = [
|
||||
":UIProcessClientEndpoint",
|
||||
":UIProcessServerEndpoint",
|
||||
":WebContentClientEndpoint",
|
||||
":WebContentServerEndpoint",
|
||||
":WebDriverClientEndpoint",
|
||||
|
@ -118,6 +142,7 @@ shared_library("LibWebView") {
|
|||
]
|
||||
sources = [
|
||||
"Attribute.cpp",
|
||||
"ChromeProcess.cpp",
|
||||
"CookieJar.cpp",
|
||||
"Database.cpp",
|
||||
"InspectorClient.cpp",
|
||||
|
@ -136,6 +161,8 @@ shared_library("LibWebView") {
|
|||
get_target_outputs(":WebContentServerEndpoint") +
|
||||
get_target_outputs(":WebDriverClientEndpoint") +
|
||||
get_target_outputs(":WebDriverServerEndpoint") +
|
||||
get_target_outputs(":UIProcessClientEndpoint") +
|
||||
get_target_outputs(":UIProcessServerEndpoint") +
|
||||
get_target_outputs(":generate_native_stylesheet_source")
|
||||
if (enable_public_suffix_list_download) {
|
||||
deps += [ ":generate_public_suffix_list_sources" ]
|
||||
|
|
|
@ -2,6 +2,7 @@ include(${SerenityOS_SOURCE_DIR}/Meta/CMake/public_suffix.cmake)
|
|||
|
||||
set(SOURCES
|
||||
Attribute.cpp
|
||||
ChromeProcess.cpp
|
||||
CookieJar.cpp
|
||||
Database.cpp
|
||||
InspectorClient.cpp
|
||||
|
@ -32,6 +33,9 @@ embed_as_string_view(
|
|||
NAMESPACE "WebView"
|
||||
)
|
||||
|
||||
compile_ipc(UIProcessServer.ipc UIProcessServerEndpoint.h)
|
||||
compile_ipc(UIProcessClient.ipc UIProcessClientEndpoint.h)
|
||||
|
||||
set(GENERATED_SOURCES
|
||||
${GENERATED_SOURCES}
|
||||
../../Services/RequestServer/RequestClientEndpoint.h
|
||||
|
@ -41,6 +45,8 @@ set(GENERATED_SOURCES
|
|||
../../Services/WebContent/WebDriverClientEndpoint.h
|
||||
../../Services/WebContent/WebDriverServerEndpoint.h
|
||||
NativeStyleSheetSource.cpp
|
||||
UIProcessClientEndpoint.h
|
||||
UIProcessServerEndpoint.h
|
||||
)
|
||||
|
||||
serenity_lib(LibWebView webview)
|
||||
|
|
123
Userland/Libraries/LibWebView/ChromeProcess.cpp
Normal file
123
Userland/Libraries/LibWebView/ChromeProcess.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <LibCore/Process.h>
|
||||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibIPC/ConnectionToServer.h>
|
||||
#include <LibWebView/ChromeProcess.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
static HashMap<int, RefPtr<UIProcessConnectionFromClient>> s_connections;
|
||||
|
||||
class UIProcessClient final
|
||||
: public IPC::ConnectionToServer<UIProcessClientEndpoint, UIProcessServerEndpoint> {
|
||||
C_OBJECT(UIProcessClient);
|
||||
|
||||
private:
|
||||
UIProcessClient(NonnullOwnPtr<Core::LocalSocket>);
|
||||
};
|
||||
|
||||
ErrorOr<ChromeProcess::ProcessDisposition> ChromeProcess::connect(Vector<ByteString> const& raw_urls, bool new_window)
|
||||
{
|
||||
static constexpr auto process_name = "Ladybird"sv;
|
||||
|
||||
auto [socket_path, pid_path] = TRY(Core::IPCProcess::paths_for_process(process_name));
|
||||
|
||||
if (auto pid = TRY(Core::IPCProcess::get_process_pid(process_name, pid_path)); pid.has_value()) {
|
||||
TRY(connect_as_client(socket_path, raw_urls, new_window));
|
||||
return ProcessDisposition::ExitProcess;
|
||||
}
|
||||
|
||||
TRY(connect_as_server(socket_path));
|
||||
|
||||
m_pid_path = pid_path;
|
||||
m_pid_file = TRY(Core::File::open(pid_path, Core::File::OpenMode::Write));
|
||||
TRY(m_pid_file->write_until_depleted(ByteString::number(::getpid())));
|
||||
|
||||
return ProcessDisposition::ContinueMainProcess;
|
||||
}
|
||||
|
||||
ErrorOr<void> ChromeProcess::connect_as_client(ByteString const& socket_path, Vector<ByteString> const& raw_urls, bool new_window)
|
||||
{
|
||||
auto socket = TRY(Core::LocalSocket::connect(socket_path));
|
||||
auto client = UIProcessClient::construct(move(socket));
|
||||
|
||||
if (new_window) {
|
||||
if (!client->send_sync_but_allow_failure<Messages::UIProcessServer::CreateNewWindow>(raw_urls))
|
||||
dbgln("Failed to send CreateNewWindow message to UIProcess");
|
||||
} else {
|
||||
if (!client->send_sync_but_allow_failure<Messages::UIProcessServer::CreateNewTab>(raw_urls))
|
||||
dbgln("Failed to send CreateNewTab message to UIProcess");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> ChromeProcess::connect_as_server(ByteString const& socket_path)
|
||||
{
|
||||
auto socket_fd = TRY(Core::IPCProcess::create_ipc_socket(socket_path));
|
||||
m_socket_path = socket_path;
|
||||
auto local_server = TRY(Core::LocalServer::try_create());
|
||||
TRY(local_server->take_over_fd(socket_fd));
|
||||
m_server_connection = TRY(IPC::MultiServer<UIProcessConnectionFromClient>::try_create(move(local_server)));
|
||||
|
||||
m_server_connection->on_new_client = [this](auto& client) {
|
||||
client.on_new_tab = [this](auto raw_urls) {
|
||||
if (this->on_new_tab)
|
||||
this->on_new_tab(raw_urls);
|
||||
};
|
||||
|
||||
client.on_new_window = [this](auto raw_urls) {
|
||||
if (this->on_new_window)
|
||||
this->on_new_window(raw_urls);
|
||||
};
|
||||
};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ChromeProcess::~ChromeProcess()
|
||||
{
|
||||
if (m_pid_file) {
|
||||
MUST(m_pid_file->truncate(0));
|
||||
MUST(Core::System::unlink(m_pid_path));
|
||||
}
|
||||
|
||||
if (!m_socket_path.is_empty())
|
||||
MUST(Core::System::unlink(m_socket_path));
|
||||
}
|
||||
|
||||
UIProcessClient::UIProcessClient(NonnullOwnPtr<Core::LocalSocket> socket)
|
||||
: IPC::ConnectionToServer<UIProcessClientEndpoint, UIProcessServerEndpoint>(*this, move(socket))
|
||||
{
|
||||
}
|
||||
|
||||
UIProcessConnectionFromClient::UIProcessConnectionFromClient(NonnullOwnPtr<Core::LocalSocket> socket, int client_id)
|
||||
: IPC::ConnectionFromClient<UIProcessClientEndpoint, UIProcessServerEndpoint>(*this, move(socket), client_id)
|
||||
{
|
||||
s_connections.set(client_id, *this);
|
||||
}
|
||||
|
||||
void UIProcessConnectionFromClient::die()
|
||||
{
|
||||
s_connections.remove(client_id());
|
||||
}
|
||||
|
||||
void UIProcessConnectionFromClient::create_new_tab(Vector<ByteString> const& urls)
|
||||
{
|
||||
if (on_new_tab)
|
||||
on_new_tab(urls);
|
||||
}
|
||||
|
||||
void UIProcessConnectionFromClient::create_new_window(Vector<ByteString> const& urls)
|
||||
{
|
||||
if (on_new_window)
|
||||
on_new_window(urls);
|
||||
}
|
||||
|
||||
}
|
69
Userland/Libraries/LibWebView/ChromeProcess.h
Normal file
69
Userland/Libraries/LibWebView/ChromeProcess.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCore/Socket.h>
|
||||
#include <LibIPC/ConnectionFromClient.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
#include <LibIPC/MultiServer.h>
|
||||
#include <LibWebView/UIProcessClientEndpoint.h>
|
||||
#include <LibWebView/UIProcessServerEndpoint.h>
|
||||
|
||||
namespace WebView {
|
||||
|
||||
class UIProcessConnectionFromClient final
|
||||
: public IPC::ConnectionFromClient<UIProcessClientEndpoint, UIProcessServerEndpoint> {
|
||||
C_OBJECT(UIProcessConnectionFromClient);
|
||||
|
||||
public:
|
||||
virtual ~UIProcessConnectionFromClient() override = default;
|
||||
|
||||
virtual void die() override;
|
||||
|
||||
Function<void(Vector<ByteString> const& urls)> on_new_tab;
|
||||
Function<void(Vector<ByteString> const& urls)> on_new_window;
|
||||
|
||||
private:
|
||||
UIProcessConnectionFromClient(NonnullOwnPtr<Core::LocalSocket>, int client_id);
|
||||
|
||||
virtual void create_new_tab(Vector<ByteString> const& urls) override;
|
||||
virtual void create_new_window(Vector<ByteString> const& urls) override;
|
||||
};
|
||||
|
||||
class ChromeProcess {
|
||||
AK_MAKE_NONCOPYABLE(ChromeProcess);
|
||||
AK_MAKE_NONMOVABLE(ChromeProcess);
|
||||
|
||||
public:
|
||||
enum class ProcessDisposition : u8 {
|
||||
ContinueMainProcess,
|
||||
ExitProcess,
|
||||
};
|
||||
|
||||
ChromeProcess() = default;
|
||||
~ChromeProcess();
|
||||
|
||||
ErrorOr<ProcessDisposition> connect(Vector<ByteString> const& raw_urls, bool new_window);
|
||||
|
||||
Function<void(Vector<ByteString> const& raw_urls)> on_new_tab;
|
||||
Function<void(Vector<ByteString> const& raw_urls)> on_new_window;
|
||||
|
||||
private:
|
||||
ErrorOr<void> connect_as_client(ByteString const& socket_path, Vector<ByteString> const& raw_urls, bool new_window);
|
||||
ErrorOr<void> connect_as_server(ByteString const& socket_path);
|
||||
|
||||
OwnPtr<IPC::MultiServer<UIProcessConnectionFromClient>> m_server_connection;
|
||||
OwnPtr<Core::File> m_pid_file;
|
||||
ByteString m_pid_path;
|
||||
ByteString m_socket_path;
|
||||
};
|
||||
|
||||
}
|
2
Userland/Libraries/LibWebView/UIProcessClient.ipc
Normal file
2
Userland/Libraries/LibWebView/UIProcessClient.ipc
Normal file
|
@ -0,0 +1,2 @@
|
|||
endpoint UIProcessClient {
|
||||
}
|
4
Userland/Libraries/LibWebView/UIProcessServer.ipc
Normal file
4
Userland/Libraries/LibWebView/UIProcessServer.ipc
Normal file
|
@ -0,0 +1,4 @@
|
|||
endpoint UIProcessServer {
|
||||
create_new_tab(Vector<ByteString> urls) => ()
|
||||
create_new_window(Vector<ByteString> urls) => ()
|
||||
}
|
Loading…
Reference in a new issue