diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 829948a752d..da40421c493 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2024, Andreas Kling + * Copyright (c) 2018-2025, Andreas Kling * Copyright (c) 2021-2023, Linus Groh * Copyright (c) 2021-2025, Luke Wilde * Copyright (c) 2021-2024, Sam Atkins @@ -6043,12 +6043,26 @@ bool Document::allow_declarative_shadow_roots() const return m_allow_declarative_shadow_roots; } +bool Document::is_render_blocking_element(GC::Ref element) const +{ + return m_render_blocking_elements.contains(element); +} + // https://html.spec.whatwg.org/multipage/dom.html#render-blocked bool Document::is_render_blocked() const { // A Document document is render-blocked if both of the following are true: // - document's render-blocking element set is non-empty, or document allows adding render-blocking elements. - // - FIXME: The current high resolution time given document's relevant global object has not exceeded an implementation-defined timeout value. + // - The current high resolution time given document's relevant global object has not exceeded an implementation-defined timeout value. + + // NOTE: This timeout is implementation-defined. + // Other browsers are willing to wait longer, but let's start with 30 seconds. + static constexpr auto max_time_to_block_rendering_in_ms = 30000.0; + + auto now = HighResolutionTime::current_high_resolution_time(relevant_global_object(*this)); + if (now > max_time_to_block_rendering_in_ms) + return false; + return !m_render_blocking_elements.is_empty() || allows_adding_render_blocking_elements(); } diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index cf636f8f223..2badbcc273c 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Andreas Kling + * Copyright (c) 2018-2025, Andreas Kling * Copyright (c) 2021-2023, Linus Groh * Copyright (c) 2023-2024, Shannon Booth * Copyright (c) 2025, Jelle Raaijmakers @@ -813,6 +813,8 @@ public: // https://html.spec.whatwg.org/multipage/dom.html#allows-adding-render-blocking-elements [[nodiscard]] bool allows_adding_render_blocking_elements() const; + [[nodiscard]] bool is_render_blocking_element(GC::Ref) const; + void add_render_blocking_element(GC::Ref); void remove_render_blocking_element(GC::Ref); diff --git a/Libraries/LibWeb/HTML/HTMLLinkElement.cpp b/Libraries/LibWeb/HTML/HTMLLinkElement.cpp index 164310b6916..0699d16b64e 100644 --- a/Libraries/LibWeb/HTML/HTMLLinkElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLLinkElement.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Andreas Kling + * Copyright (c) 2018-2025, Andreas Kling * Copyright (c) 2021, the SerenityOS developers. * Copyright (c) 2021, Sam Atkins * Copyright (c) 2023, Srikavin Ramkumar @@ -497,6 +497,8 @@ void HTMLLinkElement::process_stylesheet_resource(bool success, Fetch::Infrastru // FIXME: 2. Decrement el's node document's script-blocking style sheet counter by 1. // 7. Unblock rendering on el. + unblock_rendering(); + m_document_load_event_delayer.clear(); } @@ -526,16 +528,30 @@ bool HTMLLinkElement::stylesheet_linked_resource_fetch_setup_steps(Fetch::Infras // 3. If el's media attribute's value matches the environment and el is potentially render-blocking, then block rendering on el. // FIXME: Check media attribute value. + if (is_potentially_render_blocking()) + block_rendering(); + m_document_load_event_delayer.emplace(document()); // 4. If el is currently render-blocking, then set request's render-blocking to true. - // FIXME: Check if el is currently render-blocking. - request.set_render_blocking(true); + if (document().is_render_blocking_element(*this)) + request.set_render_blocking(true); // 5. Return true. return true; } +void HTMLLinkElement::set_parser_document(Badge, GC::Ref document) +{ + m_parser_document = document->make_weak_ptr(); +} + +bool HTMLLinkElement::is_implicitly_potentially_render_blocking() const +{ + // A link element of this type is implicitly potentially render-blocking if the element was created by its node document's parser. + return &document() == m_parser_document; +} + void HTMLLinkElement::resource_did_load_favicon() { VERIFY(m_relationship & (Relationship::Icon)); diff --git a/Libraries/LibWeb/HTML/HTMLLinkElement.h b/Libraries/LibWeb/HTML/HTMLLinkElement.h index 3b909ac8d19..675e712585c 100644 --- a/Libraries/LibWeb/HTML/HTMLLinkElement.h +++ b/Libraries/LibWeb/HTML/HTMLLinkElement.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2023, Andreas Kling + * Copyright (c) 2018-2025, Andreas Kling * Copyright (c) 2021, the SerenityOS developers. * Copyright (c) 2021, Sam Atkins * Copyright (c) 2023, Srikavin Ramkumar @@ -43,6 +43,8 @@ public: static WebIDL::ExceptionOr load_fallback_favicon_if_needed(GC::Ref); + void set_parser_document(Badge, GC::Ref); + private: HTMLLinkElement(DOM::Document&, DOM::QualifiedName); @@ -58,6 +60,7 @@ private: // ^HTMLElement virtual void visit_edges(Cell::Visitor&) override; + virtual bool is_implicitly_potentially_render_blocking() const override; struct LinkProcessingOptions { // href (default the empty string) @@ -152,6 +155,8 @@ private: bool m_explicitly_enabled { false }; Optional m_mime_type; + + WeakPtr m_parser_document; }; } diff --git a/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp b/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp index c68bb4063a1..a91be2c4357 100644 --- a/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp +++ b/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2024, Andreas Kling + * Copyright (c) 2020-2025, Andreas Kling * Copyright (c) 2021, Luke Wilde * Copyright (c) 2023-2024, Shannon Booth * @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -781,6 +782,12 @@ GC::Ref HTMLParser::create_element_for(HTMLToken const& token, Opt // 9. Let element be the result of creating an element given document, localName, given namespace, null, is, and willExecuteScript. auto element = create_element(*document, local_name, namespace_, {}, is_value, will_execute_script).release_value_but_fixme_should_propagate_errors(); + // AD-HOC: Let elements know which document they were originally parsed for. + // This is used for the render-blocking logic. + if (local_name == HTML::TagNames::link && namespace_ == Namespace::HTML) { + as(*element).set_parser_document({}, document); + } + // 10. Append each attribute in the given token to element. token.for_each_attribute([&](auto const& attribute) { DOM::QualifiedName qualified_name { attribute.local_name, attribute.prefix, attribute.namespace_ };