diff --git a/Libraries/LibWeb/WebSockets/WebSocket.cpp b/Libraries/LibWeb/WebSockets/WebSocket.cpp index 25decfc1bc7..b2345516a4e 100644 --- a/Libraries/LibWeb/WebSockets/WebSocket.cpp +++ b/Libraries/LibWeb/WebSockets/WebSocket.cpp @@ -112,6 +112,7 @@ WebIDL::ExceptionOr> WebSocket::construct_impl(JS::Realm& rea WebSocket::WebSocket(JS::Realm& realm) : EventTarget(realm) { + set_overrides_must_survive_garbage_collection(true); } WebSocket::~WebSocket() = default; @@ -122,6 +123,63 @@ void WebSocket::initialize(JS::Realm& realm) WEB_SET_PROTOTYPE_FOR_INTERFACE(WebSocket); } +// https://html.spec.whatwg.org/multipage/server-sent-events.html#garbage-collection +void WebSocket::finalize() +{ + auto ready_state = this->ready_state(); + + // If a WebSocket object is garbage collected while its connection is still open, the user agent must start the + // WebSocket closing handshake, with no status code for the Close message. [WSP] + if (ready_state != Requests::WebSocket::ReadyState::Closing && ready_state != Requests::WebSocket::ReadyState::Closed) { + // FIXME: LibProtocol does not yet support sending empty Close messages, so we use default values for now + m_websocket->close(1000); + } +} + +// https://html.spec.whatwg.org/multipage/server-sent-events.html#garbage-collection +bool WebSocket::must_survive_garbage_collection() const +{ + auto ready_state = this->ready_state(); + + // FIXME: "as of the last time the event loop reached step 1" + + // A WebSocket object whose ready state was set to CONNECTING (0) as of the last time the event loop reached step 1 + // must not be garbage collected if there are any event listeners registered for open events, message events, error + // events, or close events. + if (ready_state == Requests::WebSocket::ReadyState::Connecting) { + if (has_event_listener(HTML::EventNames::open)) + return true; + if (has_event_listener(HTML::EventNames::message)) + return true; + if (has_event_listener(HTML::EventNames::error)) + return true; + if (has_event_listener(HTML::EventNames::close)) + return true; + } + + // A WebSocket object whose ready state was set to OPEN (1) as of the last time the event loop reached step 1 must + // not be garbage collected if there are any event listeners registered for message events, error, or close events. + if (ready_state == Requests::WebSocket::ReadyState::Open) { + if (has_event_listener(HTML::EventNames::message)) + return true; + if (has_event_listener(HTML::EventNames::error)) + return true; + if (has_event_listener(HTML::EventNames::close)) + return true; + } + + // A WebSocket object whose ready state was set to CLOSING (2) as of the last time the event loop reached step 1 + // must not be garbage collected if there are any event listeners registered for error or close events. + if (ready_state == Requests::WebSocket::ReadyState::Closing) { + if (has_event_listener(HTML::EventNames::error)) + return true; + if (has_event_listener(HTML::EventNames::close)) + return true; + } + + return false; +} + ErrorOr WebSocket::establish_web_socket_connection(URL::URL const& url_record, Vector const& protocols, HTML::EnvironmentSettingsObject& client) { // FIXME: Integrate properly with FETCH as per https://fetch.spec.whatwg.org/#websocket-opening-handshake diff --git a/Libraries/LibWeb/WebSockets/WebSocket.h b/Libraries/LibWeb/WebSockets/WebSocket.h index d92f5573eaf..de770e7efe7 100644 --- a/Libraries/LibWeb/WebSockets/WebSocket.h +++ b/Libraries/LibWeb/WebSockets/WebSocket.h @@ -64,6 +64,8 @@ private: WebSocket(JS::Realm&); virtual void initialize(JS::Realm&) override; + virtual void finalize() override; + virtual bool must_survive_garbage_collection() const override; ErrorOr establish_web_socket_connection(URL::URL const& url_record, Vector const& protocols, HTML::EnvironmentSettingsObject& client);