diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index b55579a16c0..46d4873d44b 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -3905,8 +3905,11 @@ void Document::run_unloading_cleanup_steps() // 1. Let window be document's relevant global object. auto& window = as(HTML::relevant_global_object(*this)); - // FIXME: 2. For each WebSocket object webSocket whose relevant global object is window, make disappear webSocket. - // If this affected any WebSocket objects, then set document's salvageable state to false. + // 2. For each WebSocket object webSocket whose relevant global object is window, make disappear webSocket. + // If this affected any WebSocket objects, then set document's salvageable state to false. + auto affected_any_web_sockets = window.make_disappear_all_web_sockets(); + if (affected_any_web_sockets == HTML::WindowOrWorkerGlobalScopeMixin::AffectedAnyWebSockets::Yes) + m_salvageable = false; // FIXME: 3. For each WebTransport object transport whose relevant global object is window, run the context cleanup steps given transport. diff --git a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp index 222083337ee..e334051634f 100644 --- a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp +++ b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp @@ -44,6 +44,7 @@ #include #include #include +#include namespace Web::HTML { @@ -638,6 +639,28 @@ void WindowOrWorkerGlobalScopeMixin::forcibly_close_all_event_sources() event_source->forcibly_close(); } +void WindowOrWorkerGlobalScopeMixin::register_web_socket(Badge, GC::Ref web_socket) +{ + m_registered_web_sockets.append(web_socket); +} + +void WindowOrWorkerGlobalScopeMixin::unregister_web_socket(Badge, GC::Ref web_socket) +{ + m_registered_web_sockets.remove(web_socket); +} + +WindowOrWorkerGlobalScopeMixin::AffectedAnyWebSockets WindowOrWorkerGlobalScopeMixin::make_disappear_all_web_sockets() +{ + auto affected_any_web_sockets = AffectedAnyWebSockets::No; + + for (auto& web_socket : m_registered_web_sockets) { + web_socket.make_disappear(); + affected_any_web_sockets = AffectedAnyWebSockets::Yes; + } + + return affected_any_web_sockets; +} + // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#run-steps-after-a-timeout void WindowOrWorkerGlobalScopeMixin::run_steps_after_a_timeout(i32 timeout, Function completion_step) { diff --git a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h index a36f5ca57fd..a593755a55f 100644 --- a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h +++ b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace Web::HTML { @@ -63,6 +64,15 @@ public: void unregister_event_source(Badge, GC::Ref); void forcibly_close_all_event_sources(); + void register_web_socket(Badge, GC::Ref); + void unregister_web_socket(Badge, GC::Ref); + + enum class AffectedAnyWebSockets { + No, + Yes, + }; + AffectedAnyWebSockets make_disappear_all_web_sockets(); + void run_steps_after_a_timeout(i32 timeout, Function completion_step); [[nodiscard]] GC::Ref performance(); @@ -123,6 +133,8 @@ private: GC::Ptr m_crypto; bool m_error_reporting_mode { false }; + + WebSockets::WebSocket::List m_registered_web_sockets; }; } diff --git a/Libraries/LibWeb/WebSockets/WebSocket.cpp b/Libraries/LibWeb/WebSockets/WebSocket.cpp index b2345516a4e..92335ca9c3e 100644 --- a/Libraries/LibWeb/WebSockets/WebSocket.cpp +++ b/Libraries/LibWeb/WebSockets/WebSocket.cpp @@ -121,6 +121,9 @@ void WebSocket::initialize(JS::Realm& realm) { Base::initialize(realm); WEB_SET_PROTOTYPE_FOR_INTERFACE(WebSocket); + + auto& relevant_global = as(HTML::relevant_global_object(*this)); + relevant_global.register_web_socket({}, *this); } // https://html.spec.whatwg.org/multipage/server-sent-events.html#garbage-collection @@ -134,6 +137,9 @@ void WebSocket::finalize() // FIXME: LibProtocol does not yet support sending empty Close messages, so we use default values for now m_websocket->close(1000); } + + auto& relevant_global = as(HTML::relevant_global_object(*this)); + relevant_global.unregister_web_socket({}, *this); } // https://html.spec.whatwg.org/multipage/server-sent-events.html#garbage-collection @@ -395,6 +401,19 @@ void WebSocket::on_message(ByteBuffer message, bool is_text) })); } +// https://websockets.spec.whatwg.org/#make-disappear +void WebSocket::make_disappear() +{ + // -> If the WebSocket connection is not yet established [WSP] + // - Fail the WebSocket connection. [WSP] + // -> If the WebSocket closing handshake has not yet been started [WSP] + // - Start the WebSocket closing handshake, with the status code to use in the WebSocket Close message being 1001. [WSP] + // -> Otherwise + // - Do nothing. + // NOTE: All of these are handled by the WebSocket Protocol when calling close() + m_websocket->close(1001); +} + #undef __ENUMERATE #define __ENUMERATE(attribute_name, event_name) \ void WebSocket::set_##attribute_name(WebIDL::CallbackType* value) \ diff --git a/Libraries/LibWeb/WebSockets/WebSocket.h b/Libraries/LibWeb/WebSockets/WebSocket.h index de770e7efe7..d6d5446e5e8 100644 --- a/Libraries/LibWeb/WebSockets/WebSocket.h +++ b/Libraries/LibWeb/WebSockets/WebSocket.h @@ -15,7 +15,6 @@ #include #include #include -#include #include #define ENUMERATE_WEBSOCKET_EVENT_HANDLERS(E) \ @@ -55,6 +54,8 @@ public: WebIDL::ExceptionOr close(Optional code, Optional reason); WebIDL::ExceptionOr send(Variant, GC::Root, String> const& data); + void make_disappear(); + private: void on_open(); void on_message(ByteBuffer message, bool is_text); @@ -72,6 +73,11 @@ private: URL::URL m_url; String m_binary_type { "blob"_string }; RefPtr m_websocket; + + IntrusiveListNode m_list_node; + +public: + using List = IntrusiveList<&WebSocket::m_list_node>; }; }