mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
322 lines
13 KiB
C++
322 lines
13 KiB
C++
/*
|
|
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/LexicalPath.h>
|
|
#include <LibCore/ArgsParser.h>
|
|
#include <LibCore/EventLoop.h>
|
|
#include <LibCore/LocalServer.h>
|
|
#include <LibCore/Process.h>
|
|
#include <LibCore/Resource.h>
|
|
#include <LibCore/System.h>
|
|
#include <LibCore/SystemServerTakeover.h>
|
|
#include <LibCrypto/OpenSSLForward.h>
|
|
#include <LibGfx/Font/FontDatabase.h>
|
|
#include <LibGfx/Font/PathFontProvider.h>
|
|
#include <LibIPC/ConnectionFromClient.h>
|
|
#include <LibIPC/TransportHandle.h>
|
|
#include <LibJS/Bytecode/Interpreter.h>
|
|
#include <LibMain/Main.h>
|
|
#include <LibRequests/RequestClient.h>
|
|
#include <LibUnicode/TimeZone.h>
|
|
#include <LibWeb/Bindings/MainThreadVM.h>
|
|
#include <LibWeb/Fetch/Fetching/Fetching.h>
|
|
#include <LibWeb/HTML/UniversalGlobalScope.h>
|
|
#include <LibWeb/HTML/Window.h>
|
|
#include <LibWeb/Internals/Internals.h>
|
|
#include <LibWeb/Loader/ContentFilter.h>
|
|
#include <LibWeb/Loader/GeneratedPagesLoader.h>
|
|
#include <LibWeb/Loader/ResourceLoader.h>
|
|
#include <LibWeb/Painting/BackingStoreManager.h>
|
|
#include <LibWeb/Painting/PaintableBox.h>
|
|
#include <LibWeb/Platform/EventLoopPlugin.h>
|
|
#include <LibWeb/Platform/FontPlugin.h>
|
|
#include <LibWeb/WebIDL/Tracing.h>
|
|
#include <LibWebView/Plugins/ImageCodecPlugin.h>
|
|
#include <LibWebView/SiteIsolation.h>
|
|
#include <LibWebView/Utilities.h>
|
|
#include <WebContent/ConnectionFromClient.h>
|
|
#include <WebContent/PageClient.h>
|
|
#include <WebContent/WebDriverConnection.h>
|
|
|
|
#include <openssl/thread.h>
|
|
|
|
#if defined(AK_OS_MACOS)
|
|
# include <LibCore/Platform/ProcessStatisticsMach.h>
|
|
#endif
|
|
|
|
#if defined(AK_OS_WINDOWS)
|
|
# include <objbase.h>
|
|
#endif
|
|
|
|
#include <SDL3/SDL_init.h>
|
|
|
|
#if !defined(AK_OS_WINDOWS)
|
|
# include <signal.h>
|
|
static void crash_signal_handler(int signo)
|
|
{
|
|
char const* name;
|
|
switch (signo) {
|
|
case SIGSEGV:
|
|
name = "SIGSEGV";
|
|
break;
|
|
case SIGBUS:
|
|
name = "SIGBUS";
|
|
break;
|
|
case SIGFPE:
|
|
name = "SIGFPE";
|
|
break;
|
|
case SIGABRT:
|
|
name = "SIGABRT";
|
|
break;
|
|
case SIGILL:
|
|
name = "SIGILL";
|
|
break;
|
|
default:
|
|
name = "unknown";
|
|
break;
|
|
}
|
|
warnln("\n\033[31;1mCRASH\033[0m: Received signal {} ({})", name, signo);
|
|
dump_backtrace(2, 100);
|
|
signal(signo, SIG_DFL);
|
|
raise(signo);
|
|
}
|
|
|
|
static void install_crash_signal_handlers()
|
|
{
|
|
struct sigaction sa;
|
|
sa.sa_handler = crash_signal_handler;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = SA_RESETHAND;
|
|
sigaction(SIGSEGV, &sa, nullptr);
|
|
sigaction(SIGBUS, &sa, nullptr);
|
|
sigaction(SIGFPE, &sa, nullptr);
|
|
sigaction(SIGABRT, &sa, nullptr);
|
|
sigaction(SIGILL, &sa, nullptr);
|
|
}
|
|
#endif
|
|
|
|
static ErrorOr<void> load_content_filters(StringView config_path);
|
|
|
|
static ErrorOr<void> connect_to_resource_loader(GC::Heap& heap, IPC::TransportHandle const& handle);
|
|
static ErrorOr<void> connect_to_image_decoder(IPC::TransportHandle const& handle);
|
|
|
|
ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
|
{
|
|
AK::set_rich_debug_enabled(true);
|
|
|
|
#if !defined(AK_OS_WINDOWS)
|
|
install_crash_signal_handlers();
|
|
#endif
|
|
|
|
#if defined(AK_OS_WINDOWS)
|
|
// NOTE: We need this here otherwise SDL inits COM in the APARTMENTTHREADED model which we don't want as we need to
|
|
// make calls across threads which would otherwise have a high overhead. It is safe for all the objects we use.
|
|
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
|
|
VERIFY(SUCCEEDED(hr));
|
|
ScopeGuard uninitialize_com = []() { CoUninitialize(); };
|
|
#endif
|
|
// SDL is used for the Gamepad API.
|
|
if (!SDL_Init(SDL_INIT_GAMEPAD)) {
|
|
dbgln("Failed to initialize SDL3: {}", SDL_GetError());
|
|
return -1;
|
|
}
|
|
|
|
Core::EventLoop event_loop;
|
|
|
|
WebView::platform_init();
|
|
|
|
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;
|
|
bool enable_test_mode = false;
|
|
bool expose_experimental_interfaces = false;
|
|
bool expose_internals_object = false;
|
|
bool wait_for_debugger = false;
|
|
bool log_all_js_exceptions = false;
|
|
bool disable_site_isolation = false;
|
|
bool enable_idl_tracing = false;
|
|
bool enable_http_memory_cache = false;
|
|
bool force_cpu_painting = false;
|
|
bool force_fontconfig = false;
|
|
bool collect_garbage_on_every_allocation = false;
|
|
bool is_headless = false;
|
|
bool disable_scrollbar_painting = false;
|
|
StringView echo_server_port_string_view {};
|
|
StringView default_time_zone {};
|
|
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");
|
|
args_parser.add_option(expose_internals_object, "Expose internals object", "expose-internals-object");
|
|
args_parser.add_option(certificates, "Path to a certificate file", "certificate", 'C', "certificate");
|
|
args_parser.add_option(wait_for_debugger, "Wait for debugger", "wait-for-debugger");
|
|
args_parser.add_option(mach_server_name, "Mach server name", "mach-server-name", 0, "mach_server_name");
|
|
args_parser.add_option(log_all_js_exceptions, "Log all JavaScript exceptions", "log-all-js-exceptions");
|
|
args_parser.add_option(disable_site_isolation, "Disable site isolation", "disable-site-isolation");
|
|
args_parser.add_option(enable_idl_tracing, "Enable IDL tracing", "enable-idl-tracing");
|
|
args_parser.add_option(enable_http_memory_cache, "Enable HTTP cache", "enable-http-memory-cache");
|
|
args_parser.add_option(force_cpu_painting, "Force CPU painting", "force-cpu-painting");
|
|
args_parser.add_option(force_fontconfig, "Force using fontconfig for font loading", "force-fontconfig");
|
|
args_parser.add_option(collect_garbage_on_every_allocation, "Collect garbage after every JS heap allocation", "collect-garbage-on-every-allocation");
|
|
args_parser.add_option(disable_scrollbar_painting, "Don't paint horizontal or vertical viewport scrollbars", "disable-scrollbar-painting");
|
|
args_parser.add_option(echo_server_port_string_view, "Echo server port used in test internals", "echo-server-port", 0, "echo_server_port");
|
|
args_parser.add_option(is_headless, "Report that the browser is running in headless mode", "headless");
|
|
args_parser.add_option(default_time_zone, "Default time zone", "default-time-zone", 0, "time-zone-id");
|
|
args_parser.add_option(file_origins_are_tuple_origins, "Treat file:// URLs as having tuple origins", "tuple-file-origins");
|
|
|
|
args_parser.parse(arguments);
|
|
|
|
if (wait_for_debugger) {
|
|
Core::Process::wait_for_debugger_and_break();
|
|
}
|
|
|
|
if (!default_time_zone.is_empty()) {
|
|
if (auto result = Unicode::set_current_time_zone(default_time_zone); result.is_error())
|
|
dbgln("Failed to set default time zone: {}", result.error());
|
|
}
|
|
|
|
if (file_origins_are_tuple_origins)
|
|
URL::set_file_scheme_urls_have_tuple_origins();
|
|
|
|
auto& font_provider = static_cast<Gfx::PathFontProvider&>(Gfx::FontDatabase::the().install_system_font_provider(make<Gfx::PathFontProvider>()));
|
|
if (force_fontconfig) {
|
|
font_provider.set_name_but_fixme_should_create_custom_system_font_provider("FontConfig"_string);
|
|
}
|
|
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
|
|
WebContent::PageClient::set_use_skia_painter(force_cpu_painting ? WebContent::PageClient::UseSkiaPainter::CPUBackend : WebContent::PageClient::UseSkiaPainter::GPUBackendIfAvailable);
|
|
|
|
WebContent::PageClient::set_is_headless(is_headless);
|
|
|
|
if (disable_site_isolation)
|
|
WebView::disable_site_isolation();
|
|
|
|
if (enable_http_memory_cache)
|
|
Web::Fetch::Fetching::set_http_memory_cache_enabled(true);
|
|
|
|
Web::Painting::set_paint_viewport_scrollbars(!disable_scrollbar_painting);
|
|
|
|
if (!echo_server_port_string_view.is_empty()) {
|
|
if (auto maybe_echo_server_port = echo_server_port_string_view.to_number<u16>(); maybe_echo_server_port.has_value())
|
|
Web::Internals::Internals::set_echo_server_port(maybe_echo_server_port.value());
|
|
else
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
|
|
#if defined(AK_OS_MACOS)
|
|
if (!mach_server_name.is_empty()) {
|
|
auto server_port = Core::Platform::register_with_mach_server(mach_server_name);
|
|
Web::Painting::BackingStoreManager::set_browser_mach_port(move(server_port));
|
|
}
|
|
#endif
|
|
|
|
OPENSSL_TRY(OSSL_set_max_threads(nullptr, Core::System::hardware_concurrency()));
|
|
|
|
Web::HTML::Window::set_enable_test_mode(enable_test_mode);
|
|
Web::HTML::Window::set_internals_object_exposed(expose_internals_object);
|
|
Web::HTML::UniversalGlobalScopeMixin::set_experimental_interfaces_exposed(expose_experimental_interfaces);
|
|
|
|
Web::Platform::FontPlugin::install(*new Web::Platform::FontPlugin(enable_test_mode, &font_provider));
|
|
|
|
Web::Bindings::initialize_main_thread_vm(Web::Bindings::AgentType::SimilarOriginWindow);
|
|
|
|
if (collect_garbage_on_every_allocation)
|
|
Web::Bindings::main_thread_vm().heap().set_should_collect_on_every_allocation(true);
|
|
|
|
if (log_all_js_exceptions) {
|
|
JS::set_log_all_js_exceptions(true);
|
|
}
|
|
|
|
if (enable_idl_tracing) {
|
|
Web::WebIDL::set_enable_idl_tracing(true);
|
|
}
|
|
|
|
auto maybe_content_filter_error = load_content_filters(config_path);
|
|
if (maybe_content_filter_error.is_error())
|
|
dbgln("Failed to load content filters: {}", maybe_content_filter_error.error());
|
|
|
|
auto webcontent_socket = TRY(Core::take_over_socket_from_system_server("WebContent"sv));
|
|
auto webcontent_client = WebContent::ConnectionFromClient::construct(make<IPC::Transport>(move(webcontent_socket)));
|
|
|
|
auto& heap = Web::Bindings::main_thread_vm().heap();
|
|
webcontent_client->on_request_server_connection = [&heap](auto const& handle) {
|
|
if (auto result = connect_to_resource_loader(heap, handle); result.is_error())
|
|
dbgln("Failed to connect to resource loader: {}", result.error());
|
|
};
|
|
webcontent_client->on_image_decoder_connection = [](auto const& handle) {
|
|
if (auto result = connect_to_image_decoder(handle); result.is_error())
|
|
dbgln("Failed to connect to image decoder: {}", result.error());
|
|
};
|
|
|
|
return event_loop.exec();
|
|
}
|
|
|
|
static ErrorOr<void> load_content_filters(StringView config_path)
|
|
{
|
|
auto buffer = TRY(ByteBuffer::create_uninitialized(4096));
|
|
|
|
auto file = TRY(Core::File::open(ByteString::formatted("{}/BrowserContentFilters.txt", config_path), Core::File::OpenMode::Read));
|
|
auto ad_filter_list = TRY(Core::InputBufferedFile::create(move(file)));
|
|
|
|
Vector<String> patterns;
|
|
|
|
while (TRY(ad_filter_list->can_read_line())) {
|
|
auto line = TRY(ad_filter_list->read_line(buffer));
|
|
if (line.is_empty())
|
|
continue;
|
|
|
|
auto pattern = TRY(String::from_utf8(line));
|
|
TRY(patterns.try_append(move(pattern)));
|
|
}
|
|
|
|
auto& content_filter = Web::ContentFilter::the();
|
|
TRY(content_filter.set_patterns(patterns));
|
|
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> connect_to_resource_loader(GC::Heap& heap, IPC::TransportHandle const& handle)
|
|
{
|
|
auto transport = TRY(handle.create_transport());
|
|
auto request_client = TRY(try_make_ref_counted<Requests::RequestClient>(move(transport)));
|
|
#ifdef AK_OS_WINDOWS
|
|
auto response = request_client->send_sync<Messages::RequestServer::InitTransport>(Core::System::getpid());
|
|
request_client->transport().set_peer_pid(response->peer_pid());
|
|
#endif
|
|
if (Web::ResourceLoader::is_initialized())
|
|
Web::ResourceLoader::the().set_client(move(request_client));
|
|
else
|
|
Web::ResourceLoader::initialize(heap, move(request_client));
|
|
return {};
|
|
}
|
|
|
|
ErrorOr<void> connect_to_image_decoder(IPC::TransportHandle const& handle)
|
|
{
|
|
auto transport = TRY(handle.create_transport());
|
|
auto new_client = TRY(try_make_ref_counted<ImageDecoderClient::Client>(move(transport)));
|
|
#ifdef AK_OS_WINDOWS
|
|
auto response = new_client->send_sync<Messages::ImageDecoderServer::InitTransport>(Core::System::getpid());
|
|
new_client->transport().set_peer_pid(response->peer_pid());
|
|
#endif
|
|
if (Web::Platform::ImageCodecPlugin::is_initialized())
|
|
static_cast<WebView::ImageCodecPlugin&>(Web::Platform::ImageCodecPlugin::the()).set_client(move(new_client));
|
|
else
|
|
Web::Platform::ImageCodecPlugin::install(*new WebView::ImageCodecPlugin(move(new_client)));
|
|
return {};
|
|
}
|