diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 666021988fd..3a715745fe5 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -577,6 +577,7 @@ void Document::visit_edges(Cell::Visitor& visitor) visitor.visit(m_editing_host_manager); visitor.visit(m_local_storage_holder); visitor.visit(m_session_storage_holder); + visitor.visit(m_render_blocking_elements); } // https://w3c.github.io/selection-api/#dom-document-getselection @@ -5971,6 +5972,32 @@ bool Document::allow_declarative_shadow_roots() const return m_allow_declarative_shadow_roots; } +// 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. + return !m_render_blocking_elements.is_empty() || allows_adding_render_blocking_elements(); +} + +// https://html.spec.whatwg.org/multipage/dom.html#allows-adding-render-blocking-elements +bool Document::allows_adding_render_blocking_elements() const +{ + // A document allows adding render-blocking elements if document's content type is "text/html" and the body element of document is null. + return content_type() == "text/html" && !body(); +} + +void Document::add_render_blocking_element(GC::Ref element) +{ + m_render_blocking_elements.set(element); +} + +void Document::remove_render_blocking_element(GC::Ref element) +{ + m_render_blocking_elements.remove(element); +} + // https://dom.spec.whatwg.org/#document-allow-declarative-shadow-roots void Document::set_allow_declarative_shadow_roots(bool allow) { diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index e231b004d48..ad9d701b501 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -797,6 +797,14 @@ public: GC::Ptr local_storage_holder() { return m_local_storage_holder; } void set_local_storage_holder(GC::Ptr storage) { m_local_storage_holder = storage; } + // https:// html.spec.whatwg.org/multipage/dom.html#render-blocked + [[nodiscard]] bool is_render_blocked() const; + // https://html.spec.whatwg.org/multipage/dom.html#allows-adding-render-blocking-elements + [[nodiscard]] bool allows_adding_render_blocking_elements() const; + + void add_render_blocking_element(GC::Ref); + void remove_render_blocking_element(GC::Ref); + protected: virtual void initialize(JS::Realm&) override; virtual void visit_edges(Cell::Visitor&) override; @@ -1131,6 +1139,9 @@ private: // https://html.spec.whatwg.org/multipage/webstorage.html#local-storage-holder // A Document object has an associated local storage holder, which is null or a Storage object. It is initially null. GC::Ptr m_local_storage_holder; + + // https://html.spec.whatwg.org/multipage/dom.html#render-blocking-element-set + HashTable> m_render_blocking_elements; }; template<> diff --git a/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp b/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp index 9c33acefc35..f5f4fdabe36 100644 --- a/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp +++ b/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp @@ -270,7 +270,10 @@ void EventLoop::update_the_rendering() if (!document.is_fully_active()) return false; - // FIXME: doc is render-blocked; + // doc is render-blocked; + if (document.is_render_blocked()) { + return false; + } // doc's visibility state is "hidden"; if (document.hidden()) diff --git a/Libraries/LibWeb/HTML/HTMLElement.cpp b/Libraries/LibWeb/HTML/HTMLElement.cpp index 6f90ba78b70..0059947fabd 100644 --- a/Libraries/LibWeb/HTML/HTMLElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLElement.cpp @@ -71,6 +71,37 @@ void HTMLElement::visit_edges(Cell::Visitor& visitor) visitor.visit(m_popover_close_watcher); } +// https://html.spec.whatwg.org/multipage/dom.html#block-rendering +void HTMLElement::block_rendering() +{ + // 1. Let document be el's node document. + auto& document = this->document(); + + // 2. If document allows adding render-blocking elements, then append el to document's render-blocking element set. + if (document.allows_adding_render_blocking_elements()) { + document.add_render_blocking_element(*this); + } +} + +// https://html.spec.whatwg.org/multipage/dom.html#unblock-rendering +void HTMLElement::unblock_rendering() +{ + // 1. Let document be el's node document. + auto& document = this->document(); + + // 2. Remove el from document's render-blocking element set. + document.remove_render_blocking_element(*this); +} + +// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#potentially-render-blocking +bool HTMLElement::is_potentially_render_blocking() +{ + // An element is potentially render-blocking if + // FIXME: its blocking tokens set contains "render", + // or if it is implicitly potentially render-blocking, which will be defined at the individual elements. + return is_implicitly_potentially_render_blocking(); +} + // https://html.spec.whatwg.org/multipage/dom.html#dom-dir StringView HTMLElement::dir() const { diff --git a/Libraries/LibWeb/HTML/HTMLElement.h b/Libraries/LibWeb/HTML/HTMLElement.h index 230bded54e2..e36c3290076 100644 --- a/Libraries/LibWeb/HTML/HTMLElement.h +++ b/Libraries/LibWeb/HTML/HTMLElement.h @@ -151,6 +151,15 @@ protected: virtual void visit_edges(Cell::Visitor&) override; + // https://html.spec.whatwg.org/multipage/dom.html#block-rendering + void block_rendering(); + // https://html.spec.whatwg.org/multipage/dom.html#unblock-rendering + void unblock_rendering(); + // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#potentially-render-blocking + bool is_potentially_render_blocking(); + // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#implicitly-potentially-render-blocking + virtual bool is_implicitly_potentially_render_blocking() const { return false; } + private: virtual bool is_html_element() const final { return true; } diff --git a/Libraries/LibWeb/HTML/HTMLScriptElement.cpp b/Libraries/LibWeb/HTML/HTMLScriptElement.cpp index 1c820564197..a549ee1938a 100644 --- a/Libraries/LibWeb/HTML/HTMLScriptElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLScriptElement.cpp @@ -97,7 +97,8 @@ void HTMLScriptElement::execute_script() return; } - // FIXME: 3. Unblock rendering on el. + // 3. Unblock rendering on el. + unblock_rendering(); // 3. If el's result is null, then fire an event named error at el, and return. if (m_result.has()) { @@ -393,7 +394,10 @@ void HTMLScriptElement::prepare_script() return; } - // FIXME: 7. If el is potentially render-blocking, then block rendering on el. + // 7. If el is potentially render-blocking, then block rendering on el. + if (is_potentially_render_blocking()) { + block_rendering(); + } // 8. Set el's delaying the load event to true. begin_delaying_document_load_event(*m_preparation_time_document); @@ -664,4 +668,11 @@ WebIDL::ExceptionOr HTMLScriptElement::cloned(Node& copy, bool subtree) co return {}; } +// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#implicitly-potentially-render-blocking +bool HTMLScriptElement::is_implicitly_potentially_render_blocking() const +{ + // A script element el is implicitly potentially render-blocking if el's type is "classic", el is parser-inserted, and el does not have an async or defer attribute. + return m_script_type == ScriptType::Classic && is_parser_inserted() && !has_attribute(AttributeNames::async) && !has_attribute(AttributeNames::defer); +} + } diff --git a/Libraries/LibWeb/HTML/HTMLScriptElement.h b/Libraries/LibWeb/HTML/HTMLScriptElement.h index 7ab40492a38..880cbf81b11 100644 --- a/Libraries/LibWeb/HTML/HTMLScriptElement.h +++ b/Libraries/LibWeb/HTML/HTMLScriptElement.h @@ -65,6 +65,10 @@ public: virtual WebIDL::ExceptionOr cloned(Node&, bool) const override; +protected: + // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#implicitly-potentially-render-blocking + virtual bool is_implicitly_potentially_render_blocking() const override; + private: HTMLScriptElement(DOM::Document&, DOM::QualifiedName);