From e448c74736e7693677b36c9ef9bc90655c613dfe Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 24 Oct 2022 14:57:28 +0200 Subject: [PATCH] LibWeb: Protect XMLHttpRequest from GC in special circumstances The XHR gives us a set of conditions where XHR objects must survive garbage collection, even when there are no pointers to them on the heap. This patch implements those conditions using the new cell self-protection mechanism in LibJS. --- .../Libraries/LibWeb/XHR/XMLHttpRequest.cpp | 34 +++++++++++++++++++ .../Libraries/LibWeb/XHR/XMLHttpRequest.h | 1 + 2 files changed, 35 insertions(+) diff --git a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp index 2d4c99c226..e622477d62 100644 --- a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp +++ b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp @@ -51,6 +51,7 @@ XMLHttpRequest::XMLHttpRequest(HTML::Window& window) , m_window(window) , m_response_type(Bindings::XMLHttpRequestResponseType::Empty) { + set_overrides_must_survive_garbage_collection(true); set_prototype(&Bindings::cached_web_prototype(window.realm(), "XMLHttpRequest")); } @@ -587,4 +588,37 @@ WebIDL::ExceptionOr XMLHttpRequest::set_timeout(u32 timeout) // https://xhr.spec.whatwg.org/#dom-xmlhttprequest-timeout u32 XMLHttpRequest::timeout() const { return m_timeout; } +// https://xhr.spec.whatwg.org/#garbage-collection +bool XMLHttpRequest::must_survive_garbage_collection() const +{ + // An XMLHttpRequest object must not be garbage collected + // if its state is either opened with the send() flag set, headers received, or loading, + // and it has one or more event listeners registered whose type is one of + // readystatechange, progress, abort, error, load, timeout, and loadend. + if ((m_ready_state == ReadyState::Opened && m_send) + || m_ready_state == ReadyState::HeadersReceived + || m_ready_state == ReadyState::Loading) { + if (has_event_listener(EventNames::readystatechange)) + return true; + if (has_event_listener(EventNames::progress)) + return true; + if (has_event_listener(EventNames::abort)) + return true; + if (has_event_listener(EventNames::error)) + return true; + if (has_event_listener(EventNames::load)) + return true; + if (has_event_listener(EventNames::timeout)) + return true; + if (has_event_listener(EventNames::loadend)) + return true; + } + + // FIXME: If an XMLHttpRequest object is garbage collected while its connection is still open, + // the user agent must terminate the XMLHttpRequest object’s fetch controller. + // NOTE: This would go in XMLHttpRequest::finalize(). + + return false; +} + } diff --git a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h index 77037dc230..a9db911a40 100644 --- a/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h +++ b/Userland/Libraries/LibWeb/XHR/XMLHttpRequest.h @@ -65,6 +65,7 @@ public: private: virtual void visit_edges(Cell::Visitor&) override; + virtual bool must_survive_garbage_collection() const override; void set_ready_state(ReadyState); void set_status(Fetch::Infrastructure::Status status) { m_status = status; }