2022-11-14 12:09:14 -05:00
/*
2024-11-09 09:39:24 -05:00
* Copyright ( c ) 2022 - 2024 , Tim Flynn < trflynn89 @ laybird . org >
2022-11-14 12:09:14 -05:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-12-15 13:36:21 -05:00
# include <AK/Platform.h>
2022-11-14 12:09:14 -05:00
# include <LibCore/ArgsParser.h>
2022-12-15 09:18:52 -05:00
# include <LibCore/Directory.h>
2022-11-14 12:09:14 -05:00
# include <LibCore/EventLoop.h>
2023-03-24 21:58:22 +00:00
# include <LibCore/Process.h>
2022-12-15 09:18:52 -05:00
# include <LibCore/StandardPaths.h>
2022-11-14 12:09:14 -05:00
# include <LibCore/System.h>
# include <LibCore/TCPServer.h>
# include <LibMain/Main.h>
2024-10-21 19:07:55 -04:00
# include <LibWeb/WebDriver/Capabilities.h>
2024-11-10 10:26:07 -05:00
# include <LibWebView/Utilities.h>
2022-11-14 12:09:14 -05:00
# include <WebDriver/Client.h>
2024-02-13 12:25:25 -07:00
static Vector < ByteString > certificates ;
2024-11-17 20:11:13 +05:00
static ErrorOr < Core : : Process > launch_process ( StringView application , ReadonlySpan < ByteString > arguments )
2023-02-02 03:41:47 -07:00
{
2024-11-10 10:26:07 -05:00
auto paths = TRY ( WebView : : get_paths_for_helper_process ( application ) ) ;
2023-02-02 03:41:47 -07:00
2024-11-17 20:11:13 +05:00
ErrorOr < Core : : Process > result = Error : : from_string_literal ( " All paths failed to launch " ) ;
2023-02-02 03:41:47 -07:00
for ( auto const & path : paths ) {
2024-02-21 18:27:05 -07:00
auto path_view = path . view ( ) ;
2023-03-24 21:58:22 +00:00
result = Core : : Process : : spawn ( path_view , arguments , { } , Core : : Process : : KeepAsChild : : Yes ) ;
2023-02-02 03:41:47 -07:00
if ( ! result . is_error ( ) )
break ;
}
return result ;
}
2024-12-12 11:46:48 +00:00
static Vector < ByteString > create_arguments ( ByteString const & socket_path , bool force_cpu_painting , Optional < StringView > debug_process )
2022-12-15 09:18:52 -05:00
{
2024-10-21 18:55:17 -04:00
Vector < ByteString > arguments {
" --webdriver-content-path " sv ,
socket_path ,
2024-02-13 12:25:25 -07:00
} ;
Vector < ByteString > certificate_args ;
for ( auto const & certificate : certificates ) {
certificate_args . append ( ByteString : : formatted ( " --certificate={} " , certificate ) ) ;
arguments . append ( certificate_args . last ( ) . view ( ) . characters_without_null_termination ( ) ) ;
}
2024-10-21 18:55:17 -04:00
arguments . append ( " --allow-popups " sv ) ;
arguments . append ( " --force-new-process " sv ) ;
arguments . append ( " --enable-autoplay " sv ) ;
2024-12-17 14:07:21 +00:00
arguments . append ( " --disable-scrollbar-painting " sv ) ;
2024-08-01 19:42:31 +01:00
if ( force_cpu_painting )
2024-10-21 18:55:17 -04:00
arguments . append ( " --force-cpu-painting " sv ) ;
2024-06-17 10:40:50 +01:00
2024-12-12 11:46:48 +00:00
if ( debug_process . has_value ( ) )
arguments . append ( ByteString : : formatted ( " --debug-process={} " , debug_process . value ( ) ) ) ;
2025-03-11 19:25:57 -04:00
// FIXME: WebDriver does not yet handle the WebContent process switch brought by site isolation.
arguments . append ( " --disable-site-isolation " sv ) ;
2024-10-21 18:55:17 -04:00
arguments . append ( " about:blank " sv ) ;
return arguments ;
}
2024-02-13 12:25:25 -07:00
2024-12-12 11:46:48 +00:00
static ErrorOr < Core : : Process > launch_browser ( ByteString const & socket_path , bool force_cpu_painting , Optional < StringView > debug_process )
2024-10-21 18:55:17 -04:00
{
2024-12-12 11:46:48 +00:00
auto arguments = create_arguments ( socket_path , force_cpu_painting , move ( debug_process ) ) ;
2024-02-13 12:25:25 -07:00
return launch_process ( " Ladybird " sv , arguments . span ( ) ) ;
2022-12-15 09:18:52 -05:00
}
2024-12-12 11:46:48 +00:00
static ErrorOr < Core : : Process > launch_headless_browser ( ByteString const & socket_path , bool force_cpu_painting , Optional < StringView > debug_process )
2022-12-15 09:18:52 -05:00
{
2024-12-12 11:46:48 +00:00
auto arguments = create_arguments ( socket_path , force_cpu_painting , move ( debug_process ) ) ;
2024-10-21 18:55:17 -04:00
return launch_process ( " headless-browser " sv , arguments . span ( ) ) ;
2022-12-15 09:18:52 -05:00
}
2022-11-14 12:09:14 -05:00
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
{
2023-12-11 11:19:41 -07:00
AK : : set_rich_debug_enabled ( true ) ;
2022-11-14 12:09:14 -05:00
auto listen_address = " 0.0.0.0 " sv ;
int port = 8000 ;
2024-08-01 19:42:31 +01:00
bool force_cpu_painting = false ;
2024-10-21 19:07:55 -04:00
bool headless = false ;
2024-12-12 11:46:48 +00:00
Optional < StringView > debug_process ;
2022-11-14 12:09:14 -05:00
Core : : ArgsParser args_parser ;
args_parser . add_option ( listen_address , " IP address to listen on " , " listen-address " , ' l ' , " listen_address " ) ;
args_parser . add_option ( port , " Port to listen on " , " port " , ' p ' , " port " ) ;
2024-02-13 12:25:25 -07:00
args_parser . add_option ( certificates , " Path to a certificate file " , " certificate " , ' C ' , " certificate " ) ;
2024-08-01 19:42:31 +01:00
args_parser . add_option ( force_cpu_painting , " Launch browser with GPU painting disabled " , " force-cpu-painting " ) ;
2024-12-12 11:46:48 +00:00
args_parser . add_option ( debug_process , " Wait for a debugger to attach to the given process name (WebContent, RequestServer, etc.) " , " debug-process " , 0 , " process-name " ) ;
2024-10-21 19:07:55 -04:00
args_parser . add_option ( headless , " Launch browser without a graphical interface " , " headless " ) ;
2022-11-14 12:09:14 -05:00
args_parser . parse ( arguments ) ;
auto ipv4_address = IPv4Address : : from_string ( listen_address ) ;
if ( ! ipv4_address . has_value ( ) ) {
warnln ( " Invalid listen address: {} " , listen_address ) ;
return 1 ;
}
if ( ( u16 ) port ! = port ) {
warnln ( " Invalid port number: {} " , port ) ;
return 1 ;
}
2024-11-10 10:26:07 -05:00
WebView : : platform_init ( ) ;
2022-11-22 08:09:55 -05:00
2024-10-21 19:07:55 -04:00
Web : : WebDriver : : set_default_interface_mode ( headless ? Web : : WebDriver : : InterfaceMode : : Headless : Web : : WebDriver : : InterfaceMode : : Graphical ) ;
2023-12-16 17:49:34 +03:30
auto webdriver_socket_path = ByteString : : formatted ( " {}/webdriver " , TRY ( Core : : StandardPaths : : runtime_directory ( ) ) ) ;
2022-12-15 09:18:52 -05:00
TRY ( Core : : Directory : : create ( webdriver_socket_path , Core : : Directory : : CreateDirectories : : Yes ) ) ;
2022-11-14 12:09:14 -05:00
Core : : EventLoop loop ;
auto server = TRY ( Core : : TCPServer : : try_create ( ) ) ;
// FIXME: Propagate errors
server - > on_ready_to_accept = [ & ] {
auto maybe_client_socket = server - > accept ( ) ;
if ( maybe_client_socket . is_error ( ) ) {
warnln ( " Failed to accept the client: {} " , maybe_client_socket . error ( ) ) ;
return ;
}
2023-02-08 23:05:44 +01:00
auto maybe_buffered_socket = Core : : BufferedTCPSocket : : create ( maybe_client_socket . release_value ( ) ) ;
2022-11-14 12:09:14 -05:00
if ( maybe_buffered_socket . is_error ( ) ) {
warnln ( " Could not obtain a buffered socket for the client: {} " , maybe_buffered_socket . error ( ) ) ;
return ;
}
2024-07-05 20:59:21 +01:00
auto launch_browser_callback = [ & ] ( ByteString const & socket_path ) {
2024-12-12 11:46:48 +00:00
return launch_browser ( socket_path , force_cpu_painting , debug_process ) ;
2024-07-05 20:59:21 +01:00
} ;
2024-10-21 18:55:17 -04:00
auto launch_headless_browser_callback = [ & ] ( ByteString const & socket_path ) {
2024-12-12 11:46:48 +00:00
return launch_headless_browser ( socket_path , force_cpu_painting , debug_process ) ;
2024-10-21 18:55:17 -04:00
} ;
auto maybe_client = WebDriver : : Client : : try_create ( maybe_buffered_socket . release_value ( ) , { move ( launch_browser_callback ) , move ( launch_headless_browser_callback ) } , server ) ;
2022-11-14 12:09:14 -05:00
if ( maybe_client . is_error ( ) ) {
warnln ( " Could not create a WebDriver client: {} " , maybe_client . error ( ) ) ;
return ;
}
} ;
TRY ( server - > listen ( ipv4_address . value ( ) , port , Core : : TCPServer : : AllowAddressReuse : : Yes ) ) ;
outln ( " Listening on {}:{} " , ipv4_address . value ( ) , port ) ;
return loop . exec ( ) ;
}