From 5faca4f0270f3ca11b7c0d1f8c16b1e5e3e33c62 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Sun, 29 Sep 2024 23:38:49 +0200 Subject: [PATCH] LibWeb: Resolve document.fonts.ready() after fonts defined in CSS loaded This is an ad-hoc implementation that resolves the ready() promise once the document and all fonts collected by the style computer are done loading. A spec-compliant implementation would include creating a proxy CSS::FontFace for each @font-face and correctly implementing the specification steps for font fetching, but we are far from there yet. This hackish implementation should yield good WPT progress because it will actually start waiting for the Ahem font to load before capturing layout measurements. For example, it makes https://wpt.live/css/css-grid/abspos/positioned-grid-descendants-001.html go from 0/100 to 36/100 passing subtests. --- Userland/Libraries/LibWeb/CSS/FontFaceSet.cpp | 11 ++++++----- Userland/Libraries/LibWeb/CSS/FontFaceSet.h | 2 ++ Userland/Libraries/LibWeb/CSS/StyleComputer.cpp | 12 ++++++++++++ Userland/Libraries/LibWeb/CSS/StyleComputer.h | 4 ++++ .../Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp | 11 +++++++++++ 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/FontFaceSet.cpp b/Userland/Libraries/LibWeb/CSS/FontFaceSet.cpp index 767b25f94d8..783cd292669 100644 --- a/Userland/Libraries/LibWeb/CSS/FontFaceSet.cpp +++ b/Userland/Libraries/LibWeb/CSS/FontFaceSet.cpp @@ -29,9 +29,6 @@ JS::NonnullGCPtr FontFaceSet::construct_impl(JS::Realm& realm, Vect for (auto const& face : initial_faces) set_entries->set_add(face); - if (set_entries->set_size() == 0) - WebIDL::resolve_promise(realm, *ready_promise); - return realm.heap().allocate(realm, realm, ready_promise, set_entries); } @@ -44,9 +41,8 @@ FontFaceSet::FontFaceSet(JS::Realm& realm, JS::NonnullGCPtr rea : DOM::EventTarget(realm) , m_set_entries(set_entries) , m_ready_promise(ready_promise) + , m_status(Bindings::FontFaceSetLoadStatus::Loaded) { - bool const is_ready = ready()->state() == JS::Promise::State::Fulfilled; - m_status = is_ready ? Bindings::FontFaceSetLoadStatus::Loaded : Bindings::FontFaceSetLoadStatus::Loading; } void FontFaceSet::initialize(JS::Realm& realm) @@ -162,4 +158,9 @@ JS::NonnullGCPtr FontFaceSet::ready() const return verify_cast(*m_ready_promise->promise()); } +void FontFaceSet::resolve_ready_promise() +{ + WebIDL::resolve_promise(realm(), *m_ready_promise); +} + } diff --git a/Userland/Libraries/LibWeb/CSS/FontFaceSet.h b/Userland/Libraries/LibWeb/CSS/FontFaceSet.h index f365a128d1a..2b9a4d170be 100644 --- a/Userland/Libraries/LibWeb/CSS/FontFaceSet.h +++ b/Userland/Libraries/LibWeb/CSS/FontFaceSet.h @@ -43,6 +43,8 @@ public: JS::NonnullGCPtr ready() const; Bindings::FontFaceSetLoadStatus status() const { return m_status; } + void resolve_ready_promise(); + private: FontFaceSet(JS::Realm&, JS::NonnullGCPtr ready_promise, JS::NonnullGCPtr set_entries); diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index ce2033a8031..06294eafcbf 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -2863,4 +2863,16 @@ void StyleComputer::pop_ancestor(DOM::Element const& element) }); } +size_t StyleComputer::number_of_css_font_faces_with_loading_in_progress() const +{ + size_t count = 0; + for (auto const& [_, loaders] : m_loaded_fonts) { + for (auto const& loader : loaders) { + if (loader->is_loading()) + ++count; + } + } + return count; +} + } diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.h b/Userland/Libraries/LibWeb/CSS/StyleComputer.h index 21c369afc9e..9e963f881f5 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.h @@ -159,6 +159,8 @@ public: [[nodiscard]] bool has_has_selectors() const { return m_has_has_selectors; } + size_t number_of_css_font_faces_with_loading_in_progress() const; + private: enum class ComputeStyleMode { Normal, @@ -261,6 +263,8 @@ public: RefPtr font_with_point_size(float point_size); void start_loading_next_url(); + bool is_loading() const { return resource() && resource()->is_pending(); } + private: // ^ResourceClient virtual void resource_did_load() override; diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp index a3345545177..26a3ee6d080 100644 --- a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp +++ b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp @@ -8,10 +8,13 @@ #include #include #include +#include +#include #include #include #include #include +#include #include #include #include @@ -381,6 +384,14 @@ void EventLoop::process() for_each_fully_active_document_in_docs([&](DOM::Document& document) { document.process_top_layer_removals(); }); + + // Not in the spec: + for_each_fully_active_document_in_docs([&](DOM::Document& document) { + if (document.readiness() == HTML::DocumentReadyState::Complete && document.style_computer().number_of_css_font_faces_with_loading_in_progress() == 0) { + HTML::TemporaryExecutionContext context(HTML::relevant_settings_object(document), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes); + document.fonts()->resolve_ready_promise(); + } + }); } // https://html.spec.whatwg.org/multipage/webappapis.html#queue-a-task