LibWeb: Block rendering while waiting for CSS @import downloads

The implementation here is a ad-hoc, but there's no clear spec for
exactly how to handle "critical subresources" blocking rendering.

For now, this is overly conservative but fixes ugly FOUC on some
websites like https://hey.com/
This commit is contained in:
Andreas Kling
2025-11-15 14:21:20 +01:00
committed by Andreas Kling
parent 36c6079dbc
commit a94335dc5d
Notes: github-actions[bot] 2025-11-16 08:15:18 +00:00
3 changed files with 26 additions and 2 deletions

View File

@@ -123,11 +123,15 @@ void CSSImportRule::fetch()
// FIXME: Figure out the "correct" way to delay the load event.
m_document_load_event_delayer.emplace(*m_document);
// AD-HOC: Track pending import rules to block rendering until they are done.
m_document->add_pending_css_import_rule({}, *this);
// 4. Fetch a style resource from parsedUrl, with stylesheet parentStylesheet, destination "style", CORS mode "no-cors", and processResponse being the following steps given response response and byte stream, null or failure byteStream:
(void)fetch_a_style_resource(parsed_url.value(), { parent_style_sheet }, Fetch::Infrastructure::Request::Destination::Style, CorsMode::NoCors,
[strong_this = GC::Ref { *this }, parent_style_sheet = GC::Ref { parent_style_sheet }, parsed_url = parsed_url.value()](auto response, auto maybe_byte_stream) {
[strong_this = GC::Ref { *this }, parent_style_sheet = GC::Ref { parent_style_sheet }, parsed_url = parsed_url.value(), document = m_document](auto response, auto maybe_byte_stream) {
// AD-HOC: Stop delaying the load event.
ScopeGuard guard = [strong_this] {
ScopeGuard guard = [strong_this, document] {
document->remove_pending_css_import_rule({}, strong_this);
strong_this->m_document_load_event_delayer.clear();
};

View File

@@ -546,6 +546,7 @@ void Document::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
m_style_scope.visit_edges(visitor);
visitor.visit(m_pending_css_import_rules);
visitor.visit(m_page);
visitor.visit(m_window);
visitor.visit(m_layout_root);
@@ -6284,6 +6285,10 @@ bool Document::is_render_blocked() const
if (now > max_time_to_block_rendering_in_ms)
return false;
// AD-HOC: Consider pending CSS @import rules as render-blocking
if (!m_pending_css_import_rules.is_empty())
return true;
return !m_render_blocking_elements.is_empty() || allows_adding_render_blocking_elements();
}
@@ -6900,4 +6905,14 @@ StringView to_string(UpdateLayoutReason reason)
VERIFY_NOT_REACHED();
}
void Document::add_pending_css_import_rule(Badge<CSS::CSSImportRule>, GC::Ref<CSS::CSSImportRule> rule)
{
m_pending_css_import_rules.set(rule);
}
void Document::remove_pending_css_import_rule(Badge<CSS::CSSImportRule>, GC::Ref<CSS::CSSImportRule> rule)
{
m_pending_css_import_rules.remove(rule);
}
}

View File

@@ -541,6 +541,9 @@ public:
void increment_number_of_things_delaying_the_load_event(Badge<DocumentLoadEventDelayer>);
void decrement_number_of_things_delaying_the_load_event(Badge<DocumentLoadEventDelayer>);
void add_pending_css_import_rule(Badge<CSS::CSSImportRule>, GC::Ref<CSS::CSSImportRule>);
void remove_pending_css_import_rule(Badge<CSS::CSSImportRule>, GC::Ref<CSS::CSSImportRule>);
bool page_showing() const { return m_page_showing; }
void set_page_showing(bool);
@@ -1094,6 +1097,8 @@ private:
// https://html.spec.whatwg.org/multipage/semantics.html#script-blocking-style-sheet-set
HashTable<GC::Ref<DOM::Element>> m_script_blocking_style_sheet_set;
HashTable<GC::Ref<CSS::CSSImportRule>> m_pending_css_import_rules;
GC::Ptr<HTML::History> m_history;
size_t m_number_of_things_delaying_the_load_event { 0 };