diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index d70bddfc4c9..277c02833d0 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -3890,6 +3890,11 @@ void Document::destroy() return task.document() == this; }); + // AD-HOC: Mark this document as destroyed. This makes any tasks scheduled for this document in the + // future immediately runnable instead of blocking on the document becoming fully active. + // This is important because otherwise those tasks will get stuck in the task queue forever. + m_has_been_destroyed = true; + // 8. Set document's browsing context to null. m_browsing_context = nullptr; diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index 017cb78c9e1..34fa966c385 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -560,6 +560,8 @@ public: Vector> inclusive_ancestor_navigables(); Vector> document_tree_child_navigables(); + [[nodiscard]] bool has_been_destroyed() const { return m_has_been_destroyed; } + // https://html.spec.whatwg.org/multipage/document-lifecycle.html#destroy-a-document void destroy(); // https://html.spec.whatwg.org/multipage/document-lifecycle.html#destroy-a-document-and-its-descendants @@ -873,6 +875,8 @@ private: GC::Ptr m_parser; bool m_active_parser_was_aborted { false }; + bool m_has_been_destroyed { false }; + String m_source; GC::Ptr m_pending_parsing_blocking_script; diff --git a/Libraries/LibWeb/HTML/EventLoop/Task.cpp b/Libraries/LibWeb/HTML/EventLoop/Task.cpp index e71c2c81b46..a93f617074e 100644 --- a/Libraries/LibWeb/HTML/EventLoop/Task.cpp +++ b/Libraries/LibWeb/HTML/EventLoop/Task.cpp @@ -51,7 +51,15 @@ void Task::execute() bool Task::is_runnable() const { // A task is runnable if its document is either null or fully active. - return !m_document.ptr() || m_document->is_fully_active(); + if (!m_document) + return true; + + // AD-HOC: If the document has been destroyed, we'll consider the task runnable. + // Otherwise it would get stuck here forever, since a destroyed document never becomes fully active again. + if (m_document->has_been_destroyed()) + return true; + + return m_document->is_fully_active(); } DOM::Document const* Task::document() const