mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-02 06:09:08 +00:00
headless-browser: Don't go through Application to create web views
We currently create web views through the headless Application, so that the Application can store the views for easy iteration/management. We would like to move HeadlessWebView into LibWebView to make headlessness a feature of the primary Ladybird binaries. In order to do so, we need to remove this indirection, as we won't have this test-only Application class in Ladybird.
This commit is contained in:
parent
0416961133
commit
3894d8efec
Notes:
github-actions[bot]
2025-06-10 16:06:12 +00:00
Author: https://github.com/trflynn89
Commit: 3894d8efec
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5026
6 changed files with 31 additions and 70 deletions
|
@ -1,18 +1,15 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
* Copyright (c) 2024-2025, Tim Flynn <trflynn89@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <LibCore/AnonymousBuffer.h>
|
|
||||||
#include <LibCore/ArgsParser.h>
|
#include <LibCore/ArgsParser.h>
|
||||||
#include <LibCore/Environment.h>
|
#include <LibCore/Environment.h>
|
||||||
#include <LibCore/System.h>
|
#include <LibCore/System.h>
|
||||||
#include <LibWebView/HelperProcess.h>
|
|
||||||
#include <LibWebView/Utilities.h>
|
#include <LibWebView/Utilities.h>
|
||||||
#include <UI/Headless/Application.h>
|
#include <UI/Headless/Application.h>
|
||||||
#include <UI/Headless/Fixture.h>
|
#include <UI/Headless/Fixture.h>
|
||||||
#include <UI/Headless/HeadlessWebView.h>
|
|
||||||
|
|
||||||
namespace Ladybird {
|
namespace Ladybird {
|
||||||
|
|
||||||
|
@ -127,25 +124,4 @@ ErrorOr<void> Application::launch_test_fixtures()
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
HeadlessWebView& Application::create_web_view(Core::AnonymousBuffer theme, Web::DevicePixelSize window_size)
|
|
||||||
{
|
|
||||||
auto web_view = HeadlessWebView::create(move(theme), window_size);
|
|
||||||
m_web_views.append(move(web_view));
|
|
||||||
|
|
||||||
return *m_web_views.last();
|
|
||||||
}
|
|
||||||
|
|
||||||
HeadlessWebView& Application::create_child_web_view(HeadlessWebView& parent, u64 page_index)
|
|
||||||
{
|
|
||||||
auto web_view = HeadlessWebView::create_child(parent, page_index);
|
|
||||||
m_web_views.append(move(web_view));
|
|
||||||
|
|
||||||
return *m_web_views.last();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::destroy_web_views()
|
|
||||||
{
|
|
||||||
m_web_views.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
* Copyright (c) 2024-2025, Tim Flynn <trflynn89@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -8,15 +8,11 @@
|
||||||
|
|
||||||
#include <AK/ByteString.h>
|
#include <AK/ByteString.h>
|
||||||
#include <AK/Error.h>
|
#include <AK/Error.h>
|
||||||
#include <AK/NonnullOwnPtr.h>
|
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
#include <LibWeb/PixelUnits.h>
|
|
||||||
#include <LibWebView/Application.h>
|
#include <LibWebView/Application.h>
|
||||||
|
|
||||||
namespace Ladybird {
|
namespace Ladybird {
|
||||||
|
|
||||||
class HeadlessWebView;
|
|
||||||
|
|
||||||
class Application : public WebView::Application {
|
class Application : public WebView::Application {
|
||||||
WEB_VIEW_APPLICATION(Application)
|
WEB_VIEW_APPLICATION(Application)
|
||||||
|
|
||||||
|
@ -33,17 +29,6 @@ public:
|
||||||
|
|
||||||
ErrorOr<void> launch_test_fixtures();
|
ErrorOr<void> launch_test_fixtures();
|
||||||
|
|
||||||
HeadlessWebView& create_web_view(Core::AnonymousBuffer theme, Web::DevicePixelSize window_size);
|
|
||||||
HeadlessWebView& create_child_web_view(HeadlessWebView&, u64 page_index);
|
|
||||||
void destroy_web_views();
|
|
||||||
|
|
||||||
template<typename Callback>
|
|
||||||
void for_each_web_view(Callback&& callback)
|
|
||||||
{
|
|
||||||
for (auto& web_view : m_web_views)
|
|
||||||
callback(*web_view);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr u8 VERBOSITY_LEVEL_LOG_TEST_DURATION = 1;
|
static constexpr u8 VERBOSITY_LEVEL_LOG_TEST_DURATION = 1;
|
||||||
static constexpr u8 VERBOSITY_LEVEL_LOG_SLOWEST_TESTS = 2;
|
static constexpr u8 VERBOSITY_LEVEL_LOG_SLOWEST_TESTS = 2;
|
||||||
static constexpr u8 VERBOSITY_LEVEL_LOG_SKIPPED_TESTS = 3;
|
static constexpr u8 VERBOSITY_LEVEL_LOG_SKIPPED_TESTS = 3;
|
||||||
|
@ -66,9 +51,6 @@ public:
|
||||||
int per_test_timeout_in_seconds { 30 };
|
int per_test_timeout_in_seconds { 30 };
|
||||||
int width { 800 };
|
int width { 800 };
|
||||||
int height { 600 };
|
int height { 600 };
|
||||||
|
|
||||||
private:
|
|
||||||
Vector<NonnullOwnPtr<HeadlessWebView>> m_web_views;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
#include <LibGfx/Bitmap.h>
|
#include <LibGfx/Bitmap.h>
|
||||||
#include <LibGfx/ShareableBitmap.h>
|
#include <LibGfx/ShareableBitmap.h>
|
||||||
#include <UI/Headless/Application.h>
|
|
||||||
#include <UI/Headless/HeadlessWebView.h>
|
#include <UI/Headless/HeadlessWebView.h>
|
||||||
|
|
||||||
namespace Ladybird {
|
namespace Ladybird {
|
||||||
|
@ -19,13 +18,12 @@ HeadlessWebView::HeadlessWebView(Core::AnonymousBuffer theme, Web::DevicePixelSi
|
||||||
, m_test_promise(TestPromise::construct())
|
, m_test_promise(TestPromise::construct())
|
||||||
{
|
{
|
||||||
on_new_web_view = [this](auto, auto, Optional<u64> page_index) {
|
on_new_web_view = [this](auto, auto, Optional<u64> page_index) {
|
||||||
if (page_index.has_value()) {
|
auto web_view = page_index.has_value()
|
||||||
auto& web_view = Application::the().create_child_web_view(*this, *page_index);
|
? HeadlessWebView::create_child(*this, *page_index)
|
||||||
return web_view.handle();
|
: HeadlessWebView::create(m_theme, m_viewport_size);
|
||||||
}
|
|
||||||
|
|
||||||
auto& web_view = Application::the().create_web_view(m_theme, m_viewport_size);
|
m_child_web_views.append(move(web_view));
|
||||||
return web_view.handle();
|
return m_child_web_views.last()->handle();
|
||||||
};
|
};
|
||||||
|
|
||||||
on_reposition_window = [this](auto position) {
|
on_reposition_window = [this](auto position) {
|
||||||
|
|
|
@ -54,6 +54,8 @@ private:
|
||||||
|
|
||||||
// FIXME: We should implement UI-agnostic platform APIs to interact with the system clipboard.
|
// FIXME: We should implement UI-agnostic platform APIs to interact with the system clipboard.
|
||||||
Optional<Web::Clipboard::SystemClipboardItem> m_clipboard;
|
Optional<Web::Clipboard::SystemClipboardItem> m_clipboard;
|
||||||
|
|
||||||
|
Vector<NonnullOwnPtr<HeadlessWebView>> m_child_web_views;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
* Copyright (c) 2024-2025, Tim Flynn <trflynn89@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -539,9 +539,14 @@ ErrorOr<void> run_tests(Core::AnonymousBuffer const& theme, Web::DevicePixelSize
|
||||||
auto concurrency = min(app.test_concurrency, tests.size());
|
auto concurrency = min(app.test_concurrency, tests.size());
|
||||||
size_t loaded_web_views = 0;
|
size_t loaded_web_views = 0;
|
||||||
|
|
||||||
|
Vector<NonnullOwnPtr<HeadlessWebView>> views;
|
||||||
|
views.ensure_capacity(concurrency);
|
||||||
|
|
||||||
for (size_t i = 0; i < concurrency; ++i) {
|
for (size_t i = 0; i < concurrency; ++i) {
|
||||||
auto& view = app.create_web_view(theme, window_size);
|
auto view = HeadlessWebView::create(theme, window_size);
|
||||||
view.on_load_finish = [&](auto const&) { ++loaded_web_views; };
|
view->on_load_finish = [&](auto const&) { ++loaded_web_views; };
|
||||||
|
|
||||||
|
views.unchecked_append(move(view));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to wait for the initial about:blank load to complete before starting the tests, otherwise we may load the
|
// We need to wait for the initial about:blank load to complete before starting the tests, otherwise we may load the
|
||||||
|
@ -567,9 +572,9 @@ ErrorOr<void> run_tests(Core::AnonymousBuffer const& theme, Web::DevicePixelSize
|
||||||
|
|
||||||
Vector<TestCompletion> non_passing_tests;
|
Vector<TestCompletion> non_passing_tests;
|
||||||
|
|
||||||
app.for_each_web_view([&](auto& view) {
|
for (auto& view : views) {
|
||||||
set_ui_callbacks_for_tests(view);
|
set_ui_callbacks_for_tests(*view);
|
||||||
view.clear_content_filters();
|
view->clear_content_filters();
|
||||||
|
|
||||||
auto run_next_test = [&]() {
|
auto run_next_test = [&]() {
|
||||||
auto index = current_test++;
|
auto index = current_test++;
|
||||||
|
@ -589,13 +594,13 @@ ErrorOr<void> run_tests(Core::AnonymousBuffer const& theme, Web::DevicePixelSize
|
||||||
|
|
||||||
Core::deferred_invoke([&]() mutable {
|
Core::deferred_invoke([&]() mutable {
|
||||||
if (s_skipped_tests.contains_slow(test.input_path))
|
if (s_skipped_tests.contains_slow(test.input_path))
|
||||||
view.on_test_complete({ test, TestResult::Skipped });
|
view->on_test_complete({ test, TestResult::Skipped });
|
||||||
else
|
else
|
||||||
run_test(view, test, app);
|
run_test(*view, test, app);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
view.test_promise().when_resolved([&, run_next_test](auto result) {
|
view->test_promise().when_resolved([&, run_next_test](auto result) {
|
||||||
result.test.end_time = UnixDateTime::now();
|
result.test.end_time = UnixDateTime::now();
|
||||||
|
|
||||||
if (app.verbosity >= Application::VERBOSITY_LEVEL_LOG_TEST_DURATION) {
|
if (app.verbosity >= Application::VERBOSITY_LEVEL_LOG_TEST_DURATION) {
|
||||||
|
@ -636,7 +641,7 @@ ErrorOr<void> run_tests(Core::AnonymousBuffer const& theme, Web::DevicePixelSize
|
||||||
Core::deferred_invoke([run_next_test]() {
|
Core::deferred_invoke([run_next_test]() {
|
||||||
run_next_test();
|
run_next_test();
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
MUST(all_tests_complete->await());
|
MUST(all_tests_complete->await());
|
||||||
|
|
||||||
|
@ -672,15 +677,13 @@ ErrorOr<void> run_tests(Core::AnonymousBuffer const& theme, Web::DevicePixelSize
|
||||||
}
|
}
|
||||||
|
|
||||||
if (app.dump_gc_graph) {
|
if (app.dump_gc_graph) {
|
||||||
app.for_each_web_view([&](auto& view) {
|
for (auto& view : views) {
|
||||||
if (auto path = view.dump_gc_graph(); path.is_error())
|
if (auto path = view->dump_gc_graph(); path.is_error())
|
||||||
warnln("Failed to dump GC graph: {}", path.error());
|
warnln("Failed to dump GC graph: {}", path.error());
|
||||||
else
|
else
|
||||||
outln("GC graph dumped to {}", path.value());
|
outln("GC graph dumped to {}", path.value());
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
app.destroy_web_views();
|
|
||||||
|
|
||||||
if (all_tests_ok)
|
if (all_tests_ok)
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -78,22 +78,22 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& view = app->create_web_view(move(theme), window_size);
|
auto view = Ladybird::HeadlessWebView::create(move(theme), window_size);
|
||||||
|
|
||||||
VERIFY(!WebView::Application::browser_options().urls.is_empty());
|
VERIFY(!WebView::Application::browser_options().urls.is_empty());
|
||||||
auto const& url = WebView::Application::browser_options().urls.first();
|
auto const& url = WebView::Application::browser_options().urls.first();
|
||||||
|
|
||||||
if (app->dump_layout_tree || app->dump_text) {
|
if (app->dump_layout_tree || app->dump_text) {
|
||||||
Ladybird::Test test { app->dump_layout_tree ? Ladybird::TestMode::Layout : Ladybird::TestMode::Text };
|
Ladybird::Test test { app->dump_layout_tree ? Ladybird::TestMode::Layout : Ladybird::TestMode::Text };
|
||||||
Ladybird::run_dump_test(view, test, url, app->per_test_timeout_in_seconds * 1000);
|
Ladybird::run_dump_test(*view, test, url, app->per_test_timeout_in_seconds * 1000);
|
||||||
|
|
||||||
auto completion = MUST(view.test_promise().await());
|
auto completion = MUST(view->test_promise().await());
|
||||||
return completion.result == Ladybird::TestResult::Pass ? 0 : 1;
|
return completion.result == Ladybird::TestResult::Pass ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<Core::Timer> timer;
|
RefPtr<Core::Timer> timer;
|
||||||
if (!WebView::Application::browser_options().webdriver_content_ipc_path.has_value())
|
if (!WebView::Application::browser_options().webdriver_content_ipc_path.has_value())
|
||||||
timer = TRY(load_page_for_screenshot_and_exit(Core::EventLoop::current(), view, url, app->screenshot_timeout, app->screenshot_path));
|
timer = TRY(load_page_for_screenshot_and_exit(Core::EventLoop::current(), *view, url, app->screenshot_timeout, app->screenshot_path));
|
||||||
|
|
||||||
return app->execute();
|
return app->execute();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue