| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <LibCore/AnonymousBuffer.h>
 | 
					
						
							|  |  |  | #include <LibCore/ArgsParser.h>
 | 
					
						
							| 
									
										
										
										
											2025-05-31 09:43:06 -04:00
										 |  |  | #include <LibCore/Environment.h>
 | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  | #include <LibCore/System.h>
 | 
					
						
							| 
									
										
										
										
											2024-11-10 10:26:07 -05:00
										 |  |  | #include <LibWebView/HelperProcess.h>
 | 
					
						
							|  |  |  | #include <LibWebView/Utilities.h>
 | 
					
						
							| 
									
										
										
										
											2024-11-09 12:50:33 -05:00
										 |  |  | #include <UI/Headless/Application.h>
 | 
					
						
							|  |  |  | #include <UI/Headless/Fixture.h>
 | 
					
						
							|  |  |  | #include <UI/Headless/HeadlessWebView.h>
 | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace Ladybird { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Application::Application(Badge<WebView::Application>, Main::Arguments&) | 
					
						
							| 
									
										
										
										
											2024-11-10 10:26:07 -05:00
										 |  |  |     : resources_folder(WebView::s_ladybird_resource_root) | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  |     , test_concurrency(Core::System::hardware_concurrency()) | 
					
						
							| 
									
										
										
										
											2024-11-05 16:45:43 -07:00
										 |  |  |     , python_executable_path("python3") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-05 16:44:19 -07:00
										 |  |  | Application::~Application() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     for (auto& fixture : Fixture::all()) | 
					
						
							|  |  |  |         fixture->teardown(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  | void Application::create_platform_arguments(Core::ArgsParser& args_parser) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     args_parser.add_option(screenshot_timeout, "Take a screenshot after [n] seconds (default: 1)", "screenshot", 's', "n"); | 
					
						
							| 
									
										
										
										
											2025-05-20 07:19:29 -04:00
										 |  |  |     args_parser.add_option(screenshot_path, "Path the save the screenshot (default: 'output.png')", "screenshot-path", 'p', "path"); | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  |     args_parser.add_option(dump_layout_tree, "Dump layout tree and exit", "dump-layout-tree", 'd'); | 
					
						
							|  |  |  |     args_parser.add_option(dump_text, "Dump text and exit", "dump-text", 'T'); | 
					
						
							|  |  |  |     args_parser.add_option(test_concurrency, "Maximum number of tests to run at once", "test-concurrency", 'j', "jobs"); | 
					
						
							| 
									
										
										
										
											2024-11-05 16:45:43 -07:00
										 |  |  |     args_parser.add_option(python_executable_path, "Path to python3", "python-executable", 'P', "path"); | 
					
						
							| 
									
										
										
										
											2025-02-04 19:23:11 +00:00
										 |  |  |     args_parser.add_option(test_globs, "Only run tests matching the given glob", "filter", 'f', "glob"); | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  |     args_parser.add_option(test_dry_run, "List the tests that would be run, without running them", "dry-run"); | 
					
						
							|  |  |  |     args_parser.add_option(dump_failed_ref_tests, "Dump screenshots of failing ref tests", "dump-failed-ref-tests", 'D'); | 
					
						
							|  |  |  |     args_parser.add_option(dump_gc_graph, "Dump GC graph", "dump-gc-graph", 'G'); | 
					
						
							|  |  |  |     args_parser.add_option(resources_folder, "Path of the base resources folder (defaults to /res)", "resources", 'r', "resources-root-path"); | 
					
						
							|  |  |  |     args_parser.add_option(is_layout_test_mode, "Enable layout test mode", "layout-test-mode"); | 
					
						
							|  |  |  |     args_parser.add_option(rebaseline, "Rebaseline any executed layout or text tests", "rebaseline"); | 
					
						
							| 
									
										
										
										
											2024-10-26 18:22:18 +02:00
										 |  |  |     args_parser.add_option(per_test_timeout_in_seconds, "Per-test timeout (default: 30)", "per-test-timeout", 't', "seconds"); | 
					
						
							| 
									
										
										
										
											2024-11-13 22:33:12 +04:00
										 |  |  |     args_parser.add_option(width, "Set viewport width in pixels (default: 800)", "width", 'W', "pixels"); | 
					
						
							|  |  |  |     args_parser.add_option(height, "Set viewport height in pixels (default: 600)", "height", 'H', "pixels"); | 
					
						
							| 
									
										
										
										
											2024-11-30 08:51:59 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-31 09:43:06 -04:00
										 |  |  |     args_parser.add_option(Core::ArgsParser::Option { | 
					
						
							|  |  |  |         .argument_mode = Core::ArgsParser::OptionArgumentMode::Optional, | 
					
						
							|  |  |  |         .help_string = "Run tests. If a path is provided, tests are loaded from that path. Otherwise, LADYBIRD_SOURCE_DIR must be set.", | 
					
						
							|  |  |  |         .long_name = "run-tests", | 
					
						
							|  |  |  |         .short_name = 'R', | 
					
						
							|  |  |  |         .value_name = "test-root-path", | 
					
						
							|  |  |  |         .accept_value = [&](StringView value) -> ErrorOr<bool> { | 
					
						
							|  |  |  |             if (!value.is_empty()) { | 
					
						
							|  |  |  |                 test_root_path = value; | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (auto ladybird_source_dir = Core::Environment::get("LADYBIRD_SOURCE_DIR"sv); ladybird_source_dir.has_value()) { | 
					
						
							|  |  |  |                 test_root_path = LexicalPath::join(*ladybird_source_dir, "Tests"sv, "LibWeb"sv).string(); | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-30 08:51:59 -05:00
										 |  |  |     args_parser.add_option(Core::ArgsParser::Option { | 
					
						
							|  |  |  |         .argument_mode = Core::ArgsParser::OptionArgumentMode::Optional, | 
					
						
							|  |  |  |         .help_string = "Log extra information about test results (use multiple times for more information)", | 
					
						
							|  |  |  |         .long_name = "verbose", | 
					
						
							|  |  |  |         .short_name = 'v', | 
					
						
							|  |  |  |         .accept_value = [&](StringView value) -> ErrorOr<bool> { | 
					
						
							|  |  |  |             if (value.is_empty() && verbosity < NumericLimits<u8>::max()) { | 
					
						
							|  |  |  |                 ++verbosity; | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-15 16:56:52 -04:00
										 |  |  | void Application::create_platform_options(WebView::BrowserOptions& browser_options, WebView::WebContentOptions& web_content_options) | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  | { | 
					
						
							|  |  |  |     if (!test_root_path.is_empty()) { | 
					
						
							|  |  |  |         // --run-tests implies --layout-test-mode.
 | 
					
						
							|  |  |  |         is_layout_test_mode = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (is_layout_test_mode) { | 
					
						
							|  |  |  |         // Allow window.open() to succeed for tests.
 | 
					
						
							| 
									
										
										
										
											2025-03-15 16:56:52 -04:00
										 |  |  |         browser_options.allow_popups = WebView::AllowPopups::Yes; | 
					
						
							| 
									
										
										
										
											2024-10-24 19:23:51 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Ensure consistent font rendering between operating systems.
 | 
					
						
							|  |  |  |         web_content_options.force_fontconfig = WebView::ForceFontconfig::Yes; | 
					
						
							| 
									
										
										
										
											2025-04-21 11:47:24 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Ensure tests are resilient to minor changes to the viewport scrollbar.
 | 
					
						
							|  |  |  |         web_content_options.paint_viewport_scrollbars = WebView::PaintViewportScrollbars::No; | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (dump_gc_graph) { | 
					
						
							|  |  |  |         // Force all tests to run in serial if we are interested in the GC graph.
 | 
					
						
							|  |  |  |         test_concurrency = 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     web_content_options.is_layout_test_mode = is_layout_test_mode ? WebView::IsLayoutTestMode::Yes : WebView::IsLayoutTestMode::No; | 
					
						
							| 
									
										
										
										
											2024-12-09 21:51:19 +00:00
										 |  |  |     web_content_options.is_headless = WebView::IsHeadless::Yes; | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-05 16:44:19 -07:00
										 |  |  | ErrorOr<void> Application::launch_test_fixtures() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Fixture::initialize_fixtures(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // FIXME: Add option to only run specific fixtures from command line by name
 | 
					
						
							|  |  |  |     //        And an option to not run any fixtures at all
 | 
					
						
							| 
									
										
										
										
											2024-12-02 19:33:26 -06:00
										 |  |  |     for (auto const& fixture : Fixture::all()) { | 
					
						
							|  |  |  |         if (auto result = fixture->setup(web_content_options()); result.is_error()) | 
					
						
							| 
									
										
										
										
											2024-11-05 16:44:19 -07:00
										 |  |  |             return result; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-12-02 19:33:26 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-05 16:44:19 -07:00
										 |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-12 10:36:53 -05:00
										 |  |  | HeadlessWebView& Application::create_web_view(Core::AnonymousBuffer theme, Web::DevicePixelSize window_size) | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2024-10-21 18:54:25 -04:00
										 |  |  |     auto web_view = HeadlessWebView::create(move(theme), window_size); | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  |     m_web_views.append(move(web_view)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-21 18:54:25 -04:00
										 |  |  |     return *m_web_views.last(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-15 15:54:30 -06:00
										 |  |  | HeadlessWebView& Application::create_child_web_view(HeadlessWebView& parent, u64 page_index) | 
					
						
							| 
									
										
										
										
											2024-10-21 18:54:25 -04:00
										 |  |  | { | 
					
						
							|  |  |  |     auto web_view = HeadlessWebView::create_child(parent, page_index); | 
					
						
							|  |  |  |     m_web_views.append(move(web_view)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return *m_web_views.last(); | 
					
						
							| 
									
										
										
										
											2024-10-10 11:56:27 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Application::destroy_web_views() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     m_web_views.clear(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |