Tests: Wait for fonts before completing tests (WIP)

This commit is contained in:
Johan Dahlin 2024-11-19 16:12:29 +01:00
parent 5f42f1318a
commit 4c1d4aa678
17 changed files with 112 additions and 19 deletions

View file

@ -28,7 +28,7 @@ namespace Web::CSS {
GC_DEFINE_ALLOCATOR(FontFaceSet);
// https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-fontfaceset
GC::Ref<FontFaceSet> FontFaceSet::construct_impl(JS::Realm& realm, Vector<GC::Root<FontFace>> const& initial_faces)
GC::Ref<FontFaceSet> FontFaceSet::construct_impl(Web::Page& page, JS::Realm& realm, Vector<GC::Root<FontFace>> const& initial_faces)
{
auto ready_promise = WebIDL::create_promise(realm);
auto set_entries = JS::Set::create(realm);
@ -37,16 +37,17 @@ GC::Ref<FontFaceSet> FontFaceSet::construct_impl(JS::Realm& realm, Vector<GC::Ro
for (auto const& face : initial_faces)
set_entries->set_add(face);
return realm.create<FontFaceSet>(realm, ready_promise, set_entries);
return realm.create<FontFaceSet>(page, realm, ready_promise, set_entries);
}
GC::Ref<FontFaceSet> FontFaceSet::create(JS::Realm& realm)
GC::Ref<FontFaceSet> FontFaceSet::create(Web::Page& page, JS::Realm& realm)
{
return construct_impl(realm, {});
return construct_impl(page, realm, {});
}
FontFaceSet::FontFaceSet(JS::Realm& realm, GC::Ref<WebIDL::Promise> ready_promise, GC::Ref<JS::Set> set_entries)
FontFaceSet::FontFaceSet(Web::Page& page, JS::Realm& realm, GC::Ref<WebIDL::Promise> ready_promise, GC::Ref<JS::Set> set_entries)
: DOM::EventTarget(realm)
, m_page(page)
, m_set_entries(set_entries)
, m_ready_promise(ready_promise)
, m_status(Bindings::FontFaceSetLoadStatus::Loaded)
@ -63,6 +64,7 @@ void FontFaceSet::initialize(JS::Realm& realm)
void FontFaceSet::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_page);
visitor.visit(m_set_entries);
visitor.visit(m_ready_promise);
visitor.visit(m_loading_fonts);
@ -289,7 +291,10 @@ GC::Ref<WebIDL::Promise> FontFaceSet::ready() const
void FontFaceSet::resolve_ready_promise()
{
dbgln("FontFaceSet::resolve_ready_promise(): {}", m_page->url());
WebIDL::resolve_promise(realm(), m_ready_promise);
m_page->client().page_did_finish_loading_page_and_fonts(m_page->url());
}
}

View file

@ -13,6 +13,8 @@
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/CSS/FontFace.h>
#include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/HTML/TraversableNavigable.h>
#include <LibWeb/Page/Page.h>
namespace Web::CSS {
@ -21,8 +23,8 @@ class FontFaceSet final : public DOM::EventTarget {
GC_DECLARE_ALLOCATOR(FontFaceSet);
public:
[[nodiscard]] static GC::Ref<FontFaceSet> construct_impl(JS::Realm&, Vector<GC::Root<FontFace>> const& initial_faces);
[[nodiscard]] static GC::Ref<FontFaceSet> create(JS::Realm&);
[[nodiscard]] static GC::Ref<FontFaceSet> construct_impl(Web::Page&, JS::Realm&, Vector<GC::Root<FontFace>> const& initial_faces);
[[nodiscard]] static GC::Ref<FontFaceSet> create(Web::Page&, JS::Realm&);
virtual ~FontFaceSet() override = default;
GC::Ref<JS::Set> set_entries() const { return m_set_entries; }
@ -46,11 +48,12 @@ public:
void resolve_ready_promise();
private:
FontFaceSet(JS::Realm&, GC::Ref<WebIDL::Promise> ready_promise, GC::Ref<JS::Set> set_entries);
FontFaceSet(Web::Page&, JS::Realm&, GC::Ref<WebIDL::Promise> ready_promise, GC::Ref<JS::Set> set_entries);
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
GC::Ref<Web::Page> m_page;
GC::Ref<JS::Set> m_set_entries;
GC::Ref<WebIDL::Promise> m_ready_promise; // [[ReadyPromise]]

View file

@ -1606,7 +1606,7 @@ GC::Ref<HTML::HTMLAllCollection> Document::all()
GC::Ref<CSS::FontFaceSet> Document::fonts()
{
if (!m_fonts)
m_fonts = CSS::FontFaceSet::create(realm());
m_fonts = CSS::FontFaceSet::create(page(), realm());
return *m_fonts;
}

View file

@ -160,7 +160,7 @@ ENUMERATE_WORKER_GLOBAL_SCOPE_EVENT_HANDLERS(__ENUMERATE)
GC::Ref<CSS::FontFaceSet> WorkerGlobalScope::fonts()
{
if (!m_fonts)
m_fonts = CSS::FontFaceSet::create(realm());
m_fonts = CSS::FontFaceSet::create(*page(), realm());
return *m_fonts;
}

View file

@ -578,6 +578,14 @@ Vector<GC::Root<DOM::Document>> Page::documents_in_active_window() const
return documents;
}
URL::URL Page::url() const
{
if (!top_level_traversable_is_initialized())
return {};
return top_level_traversable()->active_document()->url();
}
void Page::clear_selection()
{
for (auto const& document : documents_in_active_window()) {

View file

@ -212,6 +212,8 @@ public:
FindInPageResult find_in_page_previous_match();
Optional<FindInPageQuery> last_find_in_page_query() const { return m_last_find_in_page_query; }
URL::URL url() const;
private:
explicit Page(GC::Ref<PageClient>);
virtual void visit_edges(Visitor&) override;
@ -368,6 +370,7 @@ public:
virtual void page_did_request_select_dropdown([[maybe_unused]] Web::CSSPixelPoint content_position, [[maybe_unused]] Web::CSSPixels minimum_width, [[maybe_unused]] Vector<Web::HTML::SelectItem> items) { }
virtual void page_did_finish_text_test([[maybe_unused]] String const& text) { }
virtual void page_did_finish_loading_page_and_fonts([[maybe_unused]] URL::URL const& url) { }
virtual void page_did_change_theme_color(Gfx::Color) { }

View file

@ -231,11 +231,14 @@ public:
Function<void(String const&)> on_inspector_executed_console_script;
Function<void(String const&)> on_inspector_exported_inspector_html;
Function<void()> on_web_content_crashed;
Function<void(URL::URL const&)> on_loading_page_and_fonts_finish;
virtual Web::DevicePixelSize viewport_size() const = 0;
virtual Gfx::IntPoint to_content_position(Gfx::IntPoint widget_position) const = 0;
virtual Gfx::IntPoint to_widget_position(Gfx::IntPoint content_position) const = 0;
u64 page_id() const;
protected:
static constexpr auto ZOOM_MIN_LEVEL = 0.3f;
static constexpr auto ZOOM_MAX_LEVEL = 5.0f;
@ -245,7 +248,6 @@ protected:
WebContentClient& client();
WebContentClient const& client() const;
u64 page_id() const;
virtual void update_zoom() = 0;
void handle_resize();

View file

@ -93,6 +93,15 @@ void WebContentClient::did_finish_text_test(u64 page_id, String const& text)
}
}
void WebContentClient::did_finish_loading_page_and_fonts(u64 page_id, URL::URL const& url)
{
if (auto view = view_for_page_id(page_id); view.has_value()) {
if (view->on_loading_page_and_fonts_finish)
view->on_loading_page_and_fonts_finish(url);
}
}
void WebContentClient::did_find_in_page(u64 page_id, size_t current_match_index, Optional<size_t> const& total_match_count)
{
if (auto view = view_for_page_id(page_id); view.has_value()) {

View file

@ -108,6 +108,7 @@ private:
virtual void did_request_select_dropdown(u64 page_id, Gfx::IntPoint content_position, i32 minimum_width, Vector<Web::HTML::SelectItem> const& items) override;
virtual void did_finish_handling_input_event(u64 page_id, Web::EventResult event_result) override;
virtual void did_finish_text_test(u64 page_id, String const& text) override;
virtual void did_finish_loading_page_and_fonts(u64 page_id, URL::URL const& url) override;
virtual void did_find_in_page(u64 page_id, size_t current_match_index, Optional<size_t> const& total_match_count) override;
virtual void did_change_theme_color(u64 page_id, Gfx::Color color) override;
virtual void did_insert_clipboard_entry(u64 page_id, String const& data, String const& presentation_style, String const& mime_type) override;

View file

@ -140,6 +140,7 @@ void ConnectionFromClient::update_screen_rects(u64 page_id, Vector<Web::DevicePi
void ConnectionFromClient::load_url(u64 page_id, const URL::URL& url)
{
dbgln("ConnectionFromClient::load_url({}, {})", page_id, url);
auto page = this->page(page_id);
if (!page.has_value())
return;

View file

@ -355,6 +355,12 @@ void PageClient::page_did_finish_text_test(String const& text)
client().async_did_finish_text_test(m_id, text);
}
void PageClient::page_did_finish_loading_page_and_fonts(URL::URL const& url)
{
dbgln("PageClient::page_did_finish_loading_page_and_fonts: {}", url);
client().async_did_finish_loading_page_and_fonts(m_id, url);
}
void PageClient::page_did_request_context_menu(Web::CSSPixelPoint content_position)
{
client().async_did_request_context_menu(m_id, page().css_to_device_point(content_position).to_type<int>());

View file

@ -155,6 +155,7 @@ private:
virtual void page_did_request_file_picker(Web::HTML::FileFilter accepted_file_types, Web::HTML::AllowMultipleFiles) override;
virtual void page_did_request_select_dropdown(Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector<Web::HTML::SelectItem> items) override;
virtual void page_did_finish_text_test(String const& text) override;
virtual void page_did_finish_loading_page_and_fonts(URL::URL const& url) override;
virtual void page_did_change_theme_color(Gfx::Color color) override;
virtual void page_did_insert_clipboard_entry(String data, String presentation_style, String mime_type) override;
virtual void page_did_change_audio_play_state(Web::HTML::AudioPlayState) override;

View file

@ -95,7 +95,7 @@ endpoint WebContentClient
did_get_js_console_messages(u64 page_id, i32 start_index, Vector<ByteString> message_types, Vector<ByteString> messages) =|
did_finish_text_test(u64 page_id, String text) =|
did_finish_loading_page_and_fonts(u64 page_id, URL::URL url) =|
did_find_in_page(u64 page_id, size_t current_match_index, Optional<size_t> total_match_count) =|
request_worker_agent(u64 page_id) => (IPC::File socket) // FIXME: Add required attributes to select a SharedWorker Agent

View file

@ -11,10 +11,13 @@
<body>
<div id="content"></div>
<script type="text/javascript">
window.onload = (event) => {
console.log('expectation onload', event.target.location.href);
}
document.fonts.ready.then(() => {
internals.finishLoading();
console.log('exceptation document.fonts.ready');
})
internals.disableAutomaticPageFinish();
// internals.disableAutomaticPageFinish();
document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-numeric", true, false));
</script>
</body>

View file

@ -14,10 +14,12 @@
<body>
<div id="content"></div>
<script type="text/javascript">
window.onload = (event) => {
console.log('actual onload', event.target.location.href);
}
document.fonts.ready.then(() => {
internals.finishLoading();
console.log('actual document.fonts.ready');
})
internals.disableAutomaticPageFinish();
document.getElementById("content").appendChild(createFeatureTestTable(gPropertyData, "font-variant-numeric", false, false));
</script>
</body>

View file

@ -102,6 +102,7 @@ static ErrorOr<void> collect_ref_tests(Vector<Test>& tests, StringView path, Str
static void clear_test_callbacks(HeadlessWebView& view)
{
view.on_load_finish = {};
view.on_loading_page_and_fonts_finish = {};
view.on_text_test_finish = {};
view.on_web_content_crashed = {};
}
@ -240,6 +241,7 @@ static void run_ref_test(HeadlessWebView& view, Test& test, URL::URL const& url,
{
auto timer = Core::Timer::create_single_shot(timeout_in_milliseconds, [&view, &test]() {
view.on_load_finish = {};
view.on_loading_page_and_fonts_finish = {};
view.on_text_test_finish = {};
view.on_test_complete({ test, TestResult::Timeout });
@ -288,20 +290,63 @@ static void run_ref_test(HeadlessWebView& view, Test& test, URL::URL const& url,
view.on_test_complete({ test, TestResult::Crashed });
};
view.on_load_finish = [&view, &test, on_test_complete = move(on_test_complete)](auto const&) {
view.on_load_finish = [&view, &test, &on_test_complete](auto const&) {
dbgln("on loading page: page={}, url={}", test.did_load_page, view.url().serialize_path());
test.did_load_page = true;
if (!test.did_load_fonts)
return;
//dbgln("DONE on loading page: fonts={}, {}", test.did_load_fonts, view.url());
test.did_load_page = true;
if (test.actual_screenshot) {
view.take_screenshot()->when_resolved([&test, on_test_complete = move(on_test_complete)](RefPtr<Gfx::Bitmap> screenshot) {
//dbgln("Take expectation screenshot <- this happens too early");
view.take_screenshot()->when_resolved([&test, &on_test_complete](RefPtr<Gfx::Bitmap> screenshot) {
test.expectation_screenshot = move(screenshot);
test.did_load_fonts = false;
test.did_load_page = false;
on_test_complete();
});
} else {
//dbgln("\nTake actual screenshot");
view.take_screenshot()->when_resolved([&view, &test](RefPtr<Gfx::Bitmap> screenshot) {
test.actual_screenshot = move(screenshot);
test.did_load_fonts = false;
test.did_load_page = false;
test.loading_reference_page = true;
dbgln("load-reference-page");
view.debug_request("load-reference-page");
});
}
};
// FIXME: rename to on_fonts_ready
view.on_loading_page_and_fonts_finish = [&view, &test, &on_test_complete](auto const&) {
dbgln("on fonts finish: load={}, url={}", test.did_load_fonts, view.url().serialize_path());
// This callback will be called multiple times, one for normal page load and one for the reference page load.
// We only want to take the screenshot once the reference page has loaded its fonts
if (test.loading_reference_page && !view.url().serialize_path().ends_with_bytes("-ref.html"sv))
return;
if (test.did_load_fonts)
return;
test.did_load_fonts = true;
if (!test.did_load_page)
return;
if (test.actual_screenshot) {
view.take_screenshot()->when_resolved([&test, &on_test_complete](RefPtr<Gfx::Bitmap> screenshot) {
test.expectation_screenshot = move(screenshot);
on_test_complete();
});
} else {
dbgln("\nTake actual screenshot");
view.take_screenshot()->when_resolved([&view, &test](RefPtr<Gfx::Bitmap> screenshot) {
test.actual_screenshot = move(screenshot);
test.did_load_fonts = false;
test.did_load_page = false;
test.loading_reference_page = true;
dbgln("load-reference-page");
view.debug_request("load-reference-page");
});
}
};
view.on_text_test_finish = [&](auto const&) {
dbgln("Unexpected text test finished during ref test for {}", url);
};

View file

@ -66,9 +66,13 @@ struct Test {
String text {};
bool did_finish_test { false };
bool did_finish_loading { false };
bool did_load_page { false };
bool did_load_fonts { false };
bool loading_reference_page { false };
RefPtr<Gfx::Bitmap> actual_screenshot {};
RefPtr<Gfx::Bitmap> expectation_screenshot {};
bool is_ref() const { return mode == TestMode::Ref; };
};
struct TestCompletion {