diff --git a/Libraries/LibWeb/HTML/EventLoop/Task.h b/Libraries/LibWeb/HTML/EventLoop/Task.h index 54cda396238..ed7e6be651c 100644 --- a/Libraries/LibWeb/HTML/EventLoop/Task.h +++ b/Libraries/LibWeb/HTML/EventLoop/Task.h @@ -68,6 +68,9 @@ public: // https://w3c.github.io/IndexedDB/#database-access-task-source DatabaseAccess, + // https://websockets.spec.whatwg.org/#websocket-task-source + WebSocket, + // !!! IMPORTANT: Keep this field last! // This serves as the base value of all unique task sources. // Some elements, such as the HTMLMediaElement, must have a unique task source per instance. diff --git a/Libraries/LibWeb/WebSockets/WebSocket.cpp b/Libraries/LibWeb/WebSockets/WebSocket.cpp index 930176c8c7a..754c4dc8a2c 100644 --- a/Libraries/LibWeb/WebSockets/WebSocket.cpp +++ b/Libraries/LibWeb/WebSockets/WebSocket.cpp @@ -247,28 +247,37 @@ WebIDL::ExceptionOr WebSocket::send(Variant // https://websockets.spec.whatwg.org/#feedback-from-the-protocol void WebSocket::on_open() { - // 1. Change the readyState attribute's value to OPEN (1). - // 2. Change the extensions attribute's value to the extensions in use, if it is not the null value. [WSP] - // 3. Change the protocol attribute's value to the subprotocol in use, if it is not the null value. [WSP] - dispatch_event(DOM::Event::create(realm(), HTML::EventNames::open)); + // When the WebSocket connection is established, the user agent must queue a task to run these steps: + HTML::queue_a_task(HTML::Task::Source::WebSocket, nullptr, nullptr, GC::create_function(heap(), [this] { + // 1. Change the readyState attribute's value to OPEN (1). + // 2. Change the extensions attribute's value to the extensions in use, if it is not the null value. [WSP] + // 3. Change the protocol attribute's value to the subprotocol in use, if it is not the null value. [WSP] + dispatch_event(DOM::Event::create(realm(), HTML::EventNames::open)); + })); } // https://websockets.spec.whatwg.org/#feedback-from-the-protocol void WebSocket::on_error() { - dispatch_event(DOM::Event::create(realm(), HTML::EventNames::error)); + // When the WebSocket connection is closed, possibly cleanly, the user agent must queue a task to run the following substeps: + HTML::queue_a_task(HTML::Task::Source::WebSocket, nullptr, nullptr, GC::create_function(heap(), [this] { + dispatch_event(DOM::Event::create(realm(), HTML::EventNames::error)); + })); } // https://websockets.spec.whatwg.org/#feedback-from-the-protocol void WebSocket::on_close(u16 code, String reason, bool was_clean) { - // 1. Change the readyState attribute's value to CLOSED. This is handled by the Protocol's WebSocket - // 2. If [needed], fire an event named error at the WebSocket object. This is handled by the Protocol's WebSocket - HTML::CloseEventInit event_init {}; - event_init.was_clean = was_clean; - event_init.code = code; - event_init.reason = reason; - dispatch_event(HTML::CloseEvent::create(realm(), HTML::EventNames::close, event_init)); + // When the WebSocket connection is closed, possibly cleanly, the user agent must queue a task to run the following substeps: + HTML::queue_a_task(HTML::Task::Source::WebSocket, nullptr, nullptr, GC::create_function(heap(), [this, code, reason = move(reason), was_clean] { + // 1. Change the readyState attribute's value to CLOSED. This is handled by the Protocol's WebSocket + // 2. If [needed], fire an event named error at the WebSocket object. This is handled by the Protocol's WebSocket + HTML::CloseEventInit event_init {}; + event_init.was_clean = was_clean; + event_init.code = code; + event_init.reason = reason; + dispatch_event(HTML::CloseEvent::create(realm(), HTML::EventNames::close, event_init)); + })); } // https://websockets.spec.whatwg.org/#feedback-from-the-protocol @@ -276,33 +285,37 @@ void WebSocket::on_message(ByteBuffer message, bool is_text) { if (m_websocket->ready_state() != Requests::WebSocket::ReadyState::Open) return; - if (is_text) { - auto text_message = ByteString(ReadonlyBytes(message)); - HTML::MessageEventInit event_init; - event_init.data = JS::PrimitiveString::create(vm(), text_message); - event_init.origin = url().release_value_but_fixme_should_propagate_errors(); - dispatch_event(HTML::MessageEvent::create(realm(), HTML::EventNames::message, event_init)); - return; - } - if (m_binary_type == "blob") { - // type indicates that the data is Binary and binaryType is "blob" - HTML::MessageEventInit event_init; - event_init.data = FileAPI::Blob::create(realm(), message, "text/plain;charset=utf-8"_string); - event_init.origin = url().release_value_but_fixme_should_propagate_errors(); - dispatch_event(HTML::MessageEvent::create(realm(), HTML::EventNames::message, event_init)); - return; - } else if (m_binary_type == "arraybuffer") { - // type indicates that the data is Binary and binaryType is "arraybuffer" - HTML::MessageEventInit event_init; - event_init.data = JS::ArrayBuffer::create(realm(), message); - event_init.origin = url().release_value_but_fixme_should_propagate_errors(); - dispatch_event(HTML::MessageEvent::create(realm(), HTML::EventNames::message, event_init)); - return; - } + // When a WebSocket message has been received with type type and data data, the user agent must queue a task to follow these steps: + HTML::queue_a_task(HTML::Task::Source::WebSocket, nullptr, nullptr, GC::create_function(heap(), [this, message = move(message), is_text] { + if (is_text) { + auto text_message = ByteString(ReadonlyBytes(message)); + HTML::MessageEventInit event_init; + event_init.data = JS::PrimitiveString::create(vm(), text_message); + event_init.origin = url().release_value_but_fixme_should_propagate_errors(); + dispatch_event(HTML::MessageEvent::create(realm(), HTML::EventNames::message, event_init)); + return; + } - dbgln("Unsupported WebSocket message type {}", m_binary_type); - TODO(); + if (m_binary_type == "blob") { + // type indicates that the data is Binary and binaryType is "blob" + HTML::MessageEventInit event_init; + event_init.data = FileAPI::Blob::create(realm(), message, "text/plain;charset=utf-8"_string); + event_init.origin = url().release_value_but_fixme_should_propagate_errors(); + dispatch_event(HTML::MessageEvent::create(realm(), HTML::EventNames::message, event_init)); + return; + } else if (m_binary_type == "arraybuffer") { + // type indicates that the data is Binary and binaryType is "arraybuffer" + HTML::MessageEventInit event_init; + event_init.data = JS::ArrayBuffer::create(realm(), message); + event_init.origin = url().release_value_but_fixme_should_propagate_errors(); + dispatch_event(HTML::MessageEvent::create(realm(), HTML::EventNames::message, event_init)); + return; + } + + dbgln("Unsupported WebSocket message type {}", m_binary_type); + TODO(); + })); } #undef __ENUMERATE