From 6c6fb224ec3c98e214fd33d42eaeed6a4c077107 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Tue, 30 Apr 2024 06:52:29 -0400 Subject: [PATCH] LibWeb: Add an ad-hoc ReadableStreamDefaultReader::read_all_chunks AO The ReadableStreamPipeTo AO requires reading all chunks from a stream. There actually isn't an AO defined to do that, so the "read all bytes" implementation was changed to provide each chunk in a vector in commit 12cfa08a093d0d60406d353232328ca60eff0bf5. This change makes reading all bytes a bit more uncomfortable in normal use cases, as we now have to manually join the vector we receive. This can also cause churn with huge allocations. So instead, let's just provide an ad-hoc callback to receive each chunk as they arrive. --- .../Streams/ReadableStreamDefaultReader.cpp | 24 ++++++++++++++++++- .../Streams/ReadableStreamDefaultReader.h | 7 +++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.cpp b/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.cpp index 14a47493944..08ad2bc4ac7 100644 --- a/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.cpp +++ b/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.cpp @@ -65,12 +65,13 @@ void ReadableStreamDefaultReader::visit_edges(Cell::Visitor& visitor) } // https://streams.spec.whatwg.org/#read-loop -ReadLoopReadRequest::ReadLoopReadRequest(JS::VM& vm, JS::Realm& realm, ReadableStreamDefaultReader& reader, SuccessSteps success_steps, FailureSteps failure_steps) +ReadLoopReadRequest::ReadLoopReadRequest(JS::VM& vm, JS::Realm& realm, ReadableStreamDefaultReader& reader, SuccessSteps success_steps, FailureSteps failure_steps, ChunkSteps chunk_steps) : m_vm(vm) , m_realm(realm) , m_reader(reader) , m_success_steps(move(success_steps)) , m_failure_steps(move(failure_steps)) + , m_chunk_steps(move(chunk_steps)) { } @@ -89,6 +90,11 @@ void ReadLoopReadRequest::on_chunk(JS::Value chunk) // 2. Append the bytes represented by chunk to bytes. m_byte_chunks.append(buffer); + if (m_chunk_steps) { + // FIXME: Can we move the buffer out of the `chunk`? Unclear if that is safe. + m_chunk_steps(MUST(ByteBuffer::copy(buffer))); + } + // FIXME: As the spec suggests, implement this non-recursively - instead of directly. It is not too big of a deal currently // as we enqueue the entire blob buffer in one go, meaning that we only recurse a single time. Once we begin queuing // up more than one chunk at a time, we may run into stack overflow problems. @@ -195,6 +201,22 @@ void ReadableStreamDefaultReader::read_all_bytes(ReadLoopReadRequest::SuccessSte readable_stream_default_reader_read(*this, read_request); } +void ReadableStreamDefaultReader::read_all_chunks(ReadLoopReadRequest::ChunkSteps chunk_steps, ReadLoopReadRequest::SuccessSteps success_steps, ReadLoopReadRequest::FailureSteps failure_steps) +{ + // AD-HOC: Some spec steps direct us to "read all chunks" from a stream, but there isn't an AO defined to do that. + // We implement those steps by using the "read all bytes" definition, with a custom callback to receive + // each chunk that is read. + auto& realm = this->realm(); + auto& vm = realm.vm(); + + // 1. Let readRequest be a new read request with the following items: + // NOTE: items and steps in ReadLoopReadRequest. + auto read_request = heap().allocate_without_realm(vm, realm, *this, move(success_steps), move(failure_steps), move(chunk_steps)); + + // 2. Perform ! ReadableStreamDefaultReaderRead(this, readRequest). + readable_stream_default_reader_read(*this, read_request); +} + // FIXME: This function is a promise-based wrapper around "read all bytes". The spec changed this function to not use promises // in https://github.com/whatwg/streams/commit/f894acdd417926a2121710803cef593e15127964 - however, it seems that the // FileAPI blob specification has not been updated to match, see: https://github.com/w3c/FileAPI/issues/187. diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.h b/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.h index 3bdddc1f15c..44c227d11f1 100644 --- a/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.h +++ b/Userland/Libraries/LibWeb/Streams/ReadableStreamDefaultReader.h @@ -42,7 +42,10 @@ public: // failureSteps, which is an algorithm accepting a JavaScript value using FailureSteps = JS::SafeFunction; - ReadLoopReadRequest(JS::VM& vm, JS::Realm& realm, ReadableStreamDefaultReader& reader, SuccessSteps success_steps, FailureSteps failure_steps); + // AD-HOC: callback triggered on every chunk received from the stream. + using ChunkSteps = JS::SafeFunction; + + ReadLoopReadRequest(JS::VM& vm, JS::Realm& realm, ReadableStreamDefaultReader& reader, SuccessSteps success_steps, FailureSteps failure_steps, ChunkSteps chunk_steps = {}); virtual void on_chunk(JS::Value chunk) override; @@ -59,6 +62,7 @@ private: Vector m_byte_chunks; SuccessSteps m_success_steps; FailureSteps m_failure_steps; + ChunkSteps m_chunk_steps; }; // https://streams.spec.whatwg.org/#readablestreamdefaultreader @@ -76,6 +80,7 @@ public: JS::NonnullGCPtr read(); void read_all_bytes(ReadLoopReadRequest::SuccessSteps, ReadLoopReadRequest::FailureSteps); + void read_all_chunks(ReadLoopReadRequest::ChunkSteps, ReadLoopReadRequest::SuccessSteps, ReadLoopReadRequest::FailureSteps); JS::NonnullGCPtr read_all_bytes_deprecated(); void release_lock();