LibWebView+UI: Do not create a QApplication in headless mode

This is causing errors on the WPT runner, which does not have a display
output. To do this requires shuffling around the Main::Arguments struct,
as we now need access to it from overridden WebView::Application methods
after construction.
This commit is contained in:
Timothy Flynn 2025-06-10 14:56:24 -04:00 committed by Tim Ledbetter
commit fa164083fd
Notes: github-actions[bot] 2025-06-10 22:11:59 +00:00
6 changed files with 64 additions and 49 deletions

View file

@ -85,6 +85,8 @@ Application::~Application()
void Application::initialize(Main::Arguments const& arguments) void Application::initialize(Main::Arguments const& arguments)
{ {
m_arguments = arguments;
#ifndef AK_OS_WINDOWS #ifndef AK_OS_WINDOWS
// Increase the open file limit, as the default limits on Linux cause us to run out of file descriptors with around 15 tabs open. // Increase the open file limit, as the default limits on Linux cause us to run out of file descriptors with around 15 tabs open.
if (auto result = Core::System::set_resource_limits(RLIMIT_NOFILE, 8192); result.is_error()) if (auto result = Core::System::set_resource_limits(RLIMIT_NOFILE, 8192); result.is_error())
@ -184,7 +186,7 @@ void Application::initialize(Main::Arguments const& arguments)
}); });
create_platform_arguments(args_parser); create_platform_arguments(args_parser);
args_parser.parse(arguments); args_parser.parse(m_arguments);
// Our persisted SQL storage assumes it runs in a singleton process. If we have multiple UI processes accessing // Our persisted SQL storage assumes it runs in a singleton process. If we have multiple UI processes accessing
// the same underlying database, one of them is likely to fail. // the same underlying database, one of them is likely to fail.
@ -235,7 +237,7 @@ void Application::initialize(Main::Arguments const& arguments)
m_browser_options.webdriver_content_ipc_path = *webdriver_content_ipc_path; m_browser_options.webdriver_content_ipc_path = *webdriver_content_ipc_path;
m_web_content_options = { m_web_content_options = {
.command_line = MUST(String::join(' ', arguments.strings)), .command_line = MUST(String::join(' ', m_arguments.strings)),
.executable_path = MUST(String::from_byte_string(MUST(Core::System::current_executable_path()))), .executable_path = MUST(String::from_byte_string(MUST(Core::System::current_executable_path()))),
.user_agent_preset = move(user_agent_preset), .user_agent_preset = move(user_agent_preset),
.is_layout_test_mode = layout_test_mode ? IsLayoutTestMode::Yes : IsLayoutTestMode::No, .is_layout_test_mode = layout_test_mode ? IsLayoutTestMode::Yes : IsLayoutTestMode::No,

View file

@ -72,9 +72,9 @@ public:
protected: protected:
template<DerivedFrom<Application> ApplicationType> template<DerivedFrom<Application> ApplicationType>
static NonnullOwnPtr<ApplicationType> create(Main::Arguments& arguments) static NonnullOwnPtr<ApplicationType> create(Main::Arguments const& arguments)
{ {
auto app = adopt_own(*new ApplicationType { {}, arguments }); auto app = adopt_own(*new ApplicationType { {} });
app->initialize(arguments); app->initialize(arguments);
return app; return app;
@ -90,6 +90,8 @@ protected:
virtual Optional<ByteString> ask_user_for_download_folder() const { return {}; } virtual Optional<ByteString> ask_user_for_download_folder() const { return {}; }
Main::Arguments& arguments() { return m_arguments; }
private: private:
void initialize(Main::Arguments const& arguments); void initialize(Main::Arguments const& arguments);
@ -134,6 +136,7 @@ private:
Settings m_settings; Settings m_settings;
OwnPtr<ApplicationSettingsObserver> m_settings_observer; OwnPtr<ApplicationSettingsObserver> m_settings_observer;
Main::Arguments m_arguments;
BrowserOptions m_browser_options; BrowserOptions m_browser_options;
WebContentOptions m_web_content_options; WebContentOptions m_web_content_options;
@ -157,16 +160,16 @@ private:
} }
#define WEB_VIEW_APPLICATION(ApplicationType) \ #define WEB_VIEW_APPLICATION(ApplicationType) \
public: \ public: \
static NonnullOwnPtr<ApplicationType> create(Main::Arguments& arguments) \ static NonnullOwnPtr<ApplicationType> create(Main::Arguments const& arguments) \
{ \ { \
return WebView::Application::create<ApplicationType>(arguments); \ return WebView::Application::create<ApplicationType>(arguments); \
} \ } \
\ \
static ApplicationType& the() \ static ApplicationType& the() \
{ \ { \
return static_cast<ApplicationType&>(WebView::Application::the()); \ return static_cast<ApplicationType&>(WebView::Application::the()); \
} \ } \
\ \
ApplicationType(Badge<WebView::Application>, Main::Arguments&); ApplicationType(Badge<WebView::Application>);

View file

@ -14,7 +14,7 @@
namespace TestWeb { namespace TestWeb {
Application::Application(Badge<WebView::Application>, Main::Arguments&) Application::Application(Badge<WebView::Application>)
: test_concurrency(Core::System::hardware_concurrency()) : test_concurrency(Core::System::hardware_concurrency())
, python_executable_path("python3") , python_executable_path("python3")
{ {

View file

@ -17,7 +17,7 @@
namespace Ladybird { namespace Ladybird {
Application::Application(Badge<WebView::Application>, Main::Arguments&) Application::Application(Badge<WebView::Application>)
{ {
} }

View file

@ -16,8 +16,39 @@
namespace Ladybird { namespace Ladybird {
Application::Application(Badge<WebView::Application>, Main::Arguments& arguments) class LadybirdQApplication : public QApplication {
: QApplication(arguments.argc, arguments.argv) public:
explicit LadybirdQApplication(Main::Arguments& arguments)
: QApplication(arguments.argc, arguments.argv)
{
}
virtual bool event(QEvent* event) override
{
auto& application = static_cast<Application&>(WebView::Application::the());
switch (event->type()) {
case QEvent::FileOpen: {
if (!application.on_open_file)
break;
auto const& open_event = *static_cast<QFileOpenEvent const*>(event);
auto file = ak_string_from_qstring(open_event.file());
if (auto file_url = WebView::sanitize_url(file); file_url.has_value())
application.on_open_file(file_url.release_value());
break;
}
default:
break;
}
return QApplication::event(event);
}
};
Application::Application(Badge<WebView::Application>)
{ {
} }
@ -30,7 +61,11 @@ void Application::create_platform_options(WebView::BrowserOptions&, WebView::Web
NonnullOwnPtr<Core::EventLoop> Application::create_platform_event_loop() NonnullOwnPtr<Core::EventLoop> Application::create_platform_event_loop()
{ {
Core::EventLoopManager::install(*new WebView::EventLoopManagerQt); if (!browser_options().headless_mode.has_value()) {
m_application = make<LadybirdQApplication>(arguments());
Core::EventLoopManager::install(*new WebView::EventLoopManagerQt);
}
auto event_loop = WebView::Application::create_platform_event_loop(); auto event_loop = WebView::Application::create_platform_event_loop();
if (!browser_options().headless_mode.has_value()) if (!browser_options().headless_mode.has_value())
@ -39,27 +74,6 @@ NonnullOwnPtr<Core::EventLoop> Application::create_platform_event_loop()
return event_loop; return event_loop;
} }
bool Application::event(QEvent* event)
{
switch (event->type()) {
case QEvent::FileOpen: {
if (!on_open_file)
break;
auto const& open_event = *static_cast<QFileOpenEvent const*>(event);
auto file = ak_string_from_qstring(open_event.file());
if (auto file_url = WebView::sanitize_url(file); file_url.has_value())
on_open_file(file_url.release_value());
break;
}
default:
break;
}
return QApplication::event(event);
}
BrowserWindow& Application::new_window(Vector<URL::URL> const& initial_urls, BrowserWindow::IsPopupWindow is_popup_window, Tab* parent_tab, Optional<u64> page_index) BrowserWindow& Application::new_window(Vector<URL::URL> const& initial_urls, BrowserWindow::IsPopupWindow is_popup_window, Tab* parent_tab, Optional<u64> page_index)
{ {
auto* window = new BrowserWindow(initial_urls, is_popup_window, parent_tab, move(page_index)); auto* window = new BrowserWindow(initial_urls, is_popup_window, parent_tab, move(page_index));

View file

@ -15,17 +15,12 @@
namespace Ladybird { namespace Ladybird {
class Application class Application : public WebView::Application {
: public QApplication
, public WebView::Application {
Q_OBJECT
WEB_VIEW_APPLICATION(Application) WEB_VIEW_APPLICATION(Application)
public: public:
virtual ~Application() override; virtual ~Application() override;
virtual bool event(QEvent* event) override;
Function<void(URL::URL)> on_open_file; Function<void(URL::URL)> on_open_file;
BrowserWindow& new_window(Vector<URL::URL> const& initial_urls, BrowserWindow::IsPopupWindow is_popup_window = BrowserWindow::IsPopupWindow::No, Tab* parent_tab = nullptr, Optional<u64> page_index = {}); BrowserWindow& new_window(Vector<URL::URL> const& initial_urls, BrowserWindow::IsPopupWindow is_popup_window = BrowserWindow::IsPopupWindow::No, Tab* parent_tab = nullptr, Optional<u64> page_index = {});
@ -39,6 +34,7 @@ private:
virtual Optional<ByteString> ask_user_for_download_folder() const override; virtual Optional<ByteString> ask_user_for_download_folder() const override;
OwnPtr<QApplication> m_application;
BrowserWindow* m_active_window { nullptr }; BrowserWindow* m_active_window { nullptr };
}; };