diff --git a/Libraries/LibWeb/Fetch/BodyInit.cpp b/Libraries/LibWeb/Fetch/BodyInit.cpp index b5e6e43af02..66e3a721253 100644 --- a/Libraries/LibWeb/Fetch/BodyInit.cpp +++ b/Libraries/LibWeb/Fetch/BodyInit.cpp @@ -56,7 +56,7 @@ WebIDL::ExceptionOr extract_body(JS::Realm& realm, // 4. Otherwise, set stream to a new ReadableStream object, and set up stream with byte reading support. else { stream = realm.create(realm); - Streams::set_up_readable_stream_controller_with_byte_reading_support(*stream); + stream->set_up_with_byte_reading_support(); } // 5. Assert: stream is a ReadableStream object. @@ -156,7 +156,7 @@ WebIDL::ExceptionOr extract_body(JS::Realm& realm, auto array_buffer = JS::ArrayBuffer::create(stream->realm(), move(bytes)); auto chunk = JS::Uint8Array::create(stream->realm(), array_buffer->byte_length(), *array_buffer); - Streams::readable_stream_enqueue(*stream->controller(), chunk).release_value_but_fixme_should_propagate_errors(); + stream->enqueue(chunk).release_value_but_fixme_should_propagate_errors(); } // When running action is done, close stream. diff --git a/Libraries/LibWeb/Fetch/Fetching/FetchedDataReceiver.cpp b/Libraries/LibWeb/Fetch/Fetching/FetchedDataReceiver.cpp index b03d5ff37b2..95c5f0ce34c 100644 --- a/Libraries/LibWeb/Fetch/Fetching/FetchedDataReceiver.cpp +++ b/Libraries/LibWeb/Fetch/Fetching/FetchedDataReceiver.cpp @@ -68,7 +68,7 @@ void FetchedDataReceiver::on_data_received(ReadonlyBytes bytes) HTML::TemporaryExecutionContext execution_context { m_stream->realm(), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes }; // 1. Pull from bytes buffer into stream. - if (auto result = Streams::readable_stream_pull_from_bytes(m_stream, move(bytes)); result.is_error()) { + if (auto result = m_stream->pull_from_bytes(move(bytes)); result.is_error()) { auto throw_completion = Bindings::exception_to_throw_completion(m_stream->vm(), result.release_error()); dbgln("FetchedDataReceiver: Stream error pulling bytes"); diff --git a/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp b/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp index 5fc26010b98..207f0ad619e 100644 --- a/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp +++ b/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp @@ -2305,7 +2305,7 @@ WebIDL::ExceptionOr> nonstandard_resource_loader_file_o }); // 13. Set up stream with byte reading support with pullAlgorithm set to pullAlgorithm, cancelAlgorithm set to cancelAlgorithm. - Streams::set_up_readable_stream_controller_with_byte_reading_support(stream, pull_algorithm, cancel_algorithm); + stream->set_up_with_byte_reading_support(pull_algorithm, cancel_algorithm); auto on_headers_received = GC::create_function(vm.heap(), [&vm, request, pending_response, stream](HTTP::HeaderMap const& response_headers, Optional status_code, Optional const& reason_phrase) { (void)request; diff --git a/Libraries/LibWeb/FileAPI/Blob.cpp b/Libraries/LibWeb/FileAPI/Blob.cpp index c750eb24528..dfa25d08366 100644 --- a/Libraries/LibWeb/FileAPI/Blob.cpp +++ b/Libraries/LibWeb/FileAPI/Blob.cpp @@ -324,7 +324,7 @@ GC::Ref Blob::get_stream() auto stream = realm.create(realm); // 2. Set up stream with byte reading support. - set_up_readable_stream_controller_with_byte_reading_support(stream); + stream->set_up_with_byte_reading_support(); // FIXME: 3. Run the following steps in parallel: { @@ -346,7 +346,7 @@ GC::Ref Blob::get_stream() // 3. Enqueue chunk in stream. auto maybe_error = Bindings::throw_dom_exception_if_needed(realm.vm(), [&]() { - return readable_stream_enqueue(*stream->controller(), chunk); + return stream->enqueue(chunk); }); if (maybe_error.is_error()) { diff --git a/Libraries/LibWeb/Streams/AbstractOperations.cpp b/Libraries/LibWeb/Streams/AbstractOperations.cpp index 1ca54b536d9..b34f2c16208 100644 --- a/Libraries/LibWeb/Streams/AbstractOperations.cpp +++ b/Libraries/LibWeb/Streams/AbstractOperations.cpp @@ -3259,39 +3259,6 @@ WebIDL::ExceptionOr set_up_readable_byte_stream_controller(ReadableStream& return {}; } -// https://streams.spec.whatwg.org/#readablestream-enqueue -WebIDL::ExceptionOr readable_stream_enqueue(ReadableStreamController& controller, JS::Value chunk) -{ - // 1. If stream.[[controller]] implements ReadableStreamDefaultController, - if (controller.has>()) { - // 1. Perform ! ReadableStreamDefaultControllerEnqueue(stream.[[controller]], chunk). - return readable_stream_default_controller_enqueue(controller.get>(), chunk); - } - // 2. Otherwise, - else { - // 1. Assert: stream.[[controller]] implements ReadableByteStreamController. - VERIFY(controller.has>()); - auto readable_byte_controller = controller.get>(); - - // FIXME: 2. Assert: chunk is an ArrayBufferView. - - // 3. Let byobView be the current BYOB request view for stream. - // FIXME: This is not what the spec means by 'current BYOB request view' - auto byob_view = readable_byte_controller->raw_byob_request(); - - // 4. If byobView is non-null, and chunk.[[ViewedArrayBuffer]] is byobView.[[ViewedArrayBuffer]], then: - if (byob_view) { - // FIXME: 1. Assert: chunk.[[ByteOffset]] is byobView.[[ByteOffset]]. - // FIXME: 2. Assert: chunk.[[ByteLength]] ≤ byobView.[[ByteLength]]. - // FIXME: 3. Perform ? ReadableByteStreamControllerRespond(stream.[[controller]], chunk.[[ByteLength]]). - TODO(); - } - - // 5. Otherwise, perform ? ReadableByteStreamControllerEnqueue(stream.[[controller]], chunk). - return readable_byte_stream_controller_enqueue(readable_byte_controller, chunk); - } -} - // https://streams.spec.whatwg.org/#readable-byte-stream-controller-enqueue WebIDL::ExceptionOr readable_byte_stream_controller_enqueue(ReadableByteStreamController& controller, JS::Value chunk) { @@ -3412,49 +3379,6 @@ WebIDL::ExceptionOr readable_byte_stream_controller_enqueue(ReadableByteSt return {}; } -// https://streams.spec.whatwg.org/#readablestream-pull-from-bytes -WebIDL::ExceptionOr readable_stream_pull_from_bytes(ReadableStream& stream, ByteBuffer bytes) -{ - // 1. Assert: stream.[[controller]] implements ReadableByteStreamController. - auto controller = stream.controller()->get>(); - - // 2. Let available be bytes’s length. - auto available = bytes.size(); - - // 3. Let desiredSize be available. - auto desired_size = available; - - // FIXME: 4. If stream’s current BYOB request view is non-null, then set desiredSize to stream’s current BYOB request - // view's byte length. - - // 5. Let pullSize be the smaller value of available and desiredSize. - auto pull_size = min(available, desired_size); - - // 6. Let pulled be the first pullSize bytes of bytes. - auto pulled = pull_size == available ? move(bytes) : MUST(bytes.slice(0, pull_size)); - - // 7. Remove the first pullSize bytes from bytes. - if (pull_size != available) - bytes = MUST(bytes.slice(pull_size, available - pull_size)); - - // FIXME: 8. If stream’s current BYOB request view is non-null, then: - // 1. Write pulled into stream’s current BYOB request view. - // 2. Perform ? ReadableByteStreamControllerRespond(stream.[[controller]], pullSize). - // 9. Otherwise, - { - auto& realm = HTML::relevant_realm(stream); - - // 1. Set view to the result of creating a Uint8Array from pulled in stream’s relevant Realm. - auto array_buffer = JS::ArrayBuffer::create(realm, move(pulled)); - auto view = JS::Uint8Array::create(realm, array_buffer->byte_length(), *array_buffer); - - // 2. Perform ? ReadableByteStreamControllerEnqueue(stream.[[controller]], view). - TRY(readable_byte_stream_controller_enqueue(controller, view)); - } - - return {}; -} - // https://streams.spec.whatwg.org/#transfer-array-buffer WebIDL::ExceptionOr> transfer_array_buffer(JS::Realm& realm, JS::ArrayBuffer& buffer) { @@ -3642,52 +3566,6 @@ PullIntoDescriptor readable_byte_stream_controller_shift_pending_pull_into(Reada return descriptor; } -// https://streams.spec.whatwg.org/#readablestream-set-up-with-byte-reading-support -void set_up_readable_stream_controller_with_byte_reading_support(ReadableStream& stream, GC::Ptr pull_algorithm, GC::Ptr cancel_algorithm, double high_water_mark) -{ - auto& realm = stream.realm(); - - // 1. Let startAlgorithm be an algorithm that returns undefined. - auto start_algorithm = GC::create_function(realm.heap(), []() -> WebIDL::ExceptionOr { return JS::js_undefined(); }); - - // 2. Let pullAlgorithmWrapper be an algorithm that runs these steps: - auto pull_algorithm_wrapper = GC::create_function(realm.heap(), [&realm, pull_algorithm]() { - // 1. Let result be the result of running pullAlgorithm, if pullAlgorithm was given, or null otherwise. If this throws an exception e, return a promise rejected with e. - GC::Ptr result = nullptr; - if (pull_algorithm) - result = pull_algorithm->function()(); - - // 2. If result is a Promise, then return result. - if (result != nullptr) - return GC::Ref(*result); - - // 3. Return a promise resolved with undefined. - return WebIDL::create_resolved_promise(realm, JS::js_undefined()); - }); - - // 3. Let cancelAlgorithmWrapper be an algorithm that runs these steps: - auto cancel_algorithm_wrapper = GC::create_function(realm.heap(), [&realm, cancel_algorithm](JS::Value c) { - // 1. Let result be the result of running cancelAlgorithm, if cancelAlgorithm was given, or null otherwise. If this throws an exception e, return a promise rejected with e. - GC::Ptr result = nullptr; - if (cancel_algorithm) - result = cancel_algorithm->function()(c); - - // 2. If result is a Promise, then return result. - if (result != nullptr) - return GC::Ref(*result); - - // 3. Return a promise resolved with undefined. - return WebIDL::create_resolved_promise(realm, JS::js_undefined()); - }); - - // 4. Perform ! InitializeReadableStream(stream). - // 5. Let controller be a new ReadableByteStreamController. - auto controller = realm.create(realm); - - // 6. Perform ! SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithmWrapper, cancelAlgorithmWrapper, highWaterMark, undefined). - MUST(set_up_readable_byte_stream_controller(stream, controller, start_algorithm, pull_algorithm_wrapper, cancel_algorithm_wrapper, high_water_mark, JS::js_undefined())); -} - // https://streams.spec.whatwg.org/#writable-stream-abort GC::Ref writable_stream_abort(WritableStream& stream, JS::Value reason) { diff --git a/Libraries/LibWeb/Streams/AbstractOperations.h b/Libraries/LibWeb/Streams/AbstractOperations.h index 11d143558e7..dceac54b5c9 100644 --- a/Libraries/LibWeb/Streams/AbstractOperations.h +++ b/Libraries/LibWeb/Streams/AbstractOperations.h @@ -74,7 +74,6 @@ Optional readable_stream_default_controller_get_desired_size(ReadableStr bool readable_stream_default_controller_can_close_or_enqueue(ReadableStreamDefaultController&); WebIDL::ExceptionOr set_up_readable_stream_default_controller(ReadableStream&, ReadableStreamDefaultController&, GC::Ref, GC::Ref, GC::Ref, double high_water_mark, GC::Ref); WebIDL::ExceptionOr set_up_readable_stream_default_controller_from_underlying_source(ReadableStream&, JS::Value underlying_source_value, UnderlyingSource, double high_water_mark, GC::Ref); -void set_up_readable_stream_controller_with_byte_reading_support(ReadableStream&, GC::Ptr = {}, GC::Ptr = {}, double high_water_mark = 0); WebIDL::ExceptionOr set_up_readable_byte_stream_controller(ReadableStream&, ReadableByteStreamController&, GC::Ref, GC::Ref, GC::Ref, double high_water_mark, JS::Value auto_allocate_chunk_size); WebIDL::ExceptionOr set_up_readable_byte_stream_controller_from_underlying_source(ReadableStream&, JS::Value underlying_source, UnderlyingSource const& underlying_source_dict, double high_water_mark); GC::Ptr readable_byte_stream_controller_get_byob_request(GC::Ref); @@ -85,9 +84,7 @@ WebIDL::ExceptionOr readable_byte_stream_controller_respond_internal(Reada WebIDL::ExceptionOr readable_byte_stream_controller_respond(ReadableByteStreamController&, u64 bytes_written); WebIDL::ExceptionOr readable_byte_stream_controller_respond_with_new_view(JS::Realm&, ReadableByteStreamController&, WebIDL::ArrayBufferView&); -WebIDL::ExceptionOr readable_stream_enqueue(ReadableStreamController& controller, JS::Value chunk); WebIDL::ExceptionOr readable_byte_stream_controller_enqueue(ReadableByteStreamController& controller, JS::Value chunk); -WebIDL::ExceptionOr readable_stream_pull_from_bytes(ReadableStream&, ByteBuffer bytes); WebIDL::ExceptionOr> transfer_array_buffer(JS::Realm& realm, JS::ArrayBuffer& buffer); WebIDL::ExceptionOr readable_byte_stream_controller_enqueue_detached_pull_into_queue(ReadableByteStreamController& controller, PullIntoDescriptor& pull_into_descriptor); void readable_byte_stream_controller_commit_pull_into_descriptor(ReadableStream&, PullIntoDescriptor const&); diff --git a/Libraries/LibWeb/Streams/ReadableStream.cpp b/Libraries/LibWeb/Streams/ReadableStream.cpp index fc19a119d90..03510479307 100644 --- a/Libraries/LibWeb/Streams/ReadableStream.cpp +++ b/Libraries/LibWeb/Streams/ReadableStream.cpp @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -258,4 +259,128 @@ bool ReadableStream::is_disturbed() const return m_disturbed; } +// https://streams.spec.whatwg.org/#readablestream-pull-from-bytes +WebIDL::ExceptionOr ReadableStream::pull_from_bytes(ByteBuffer bytes) +{ + auto& realm = this->realm(); + + // 1. Assert: stream.[[controller]] implements ReadableByteStreamController. + auto& controller = this->controller()->get>(); + + // 2. Let available be bytes’s length. + auto available = bytes.size(); + + // 3. Let desiredSize be available. + auto desired_size = available; + + // FIXME: 4. If stream’s current BYOB request view is non-null, then set desiredSize to stream’s current BYOB request + // view's byte length. + + // 5. Let pullSize be the smaller value of available and desiredSize. + auto pull_size = min(available, desired_size); + + // 6. Let pulled be the first pullSize bytes of bytes. + auto pulled = pull_size == available ? move(bytes) : MUST(bytes.slice(0, pull_size)); + + // 7. Remove the first pullSize bytes from bytes. + if (pull_size != available) + bytes = MUST(bytes.slice(pull_size, available - pull_size)); + + // FIXME: 8. If stream’s current BYOB request view is non-null, then: + // 1. Write pulled into stream’s current BYOB request view. + // 2. Perform ? ReadableByteStreamControllerRespond(stream.[[controller]], pullSize). + // 9. Otherwise, + { + // 1. Set view to the result of creating a Uint8Array from pulled in stream’s relevant Realm. + auto array_buffer = JS::ArrayBuffer::create(realm, move(pulled)); + auto view = JS::Uint8Array::create(realm, array_buffer->byte_length(), *array_buffer); + + // 2. Perform ? ReadableByteStreamControllerEnqueue(stream.[[controller]], view). + TRY(readable_byte_stream_controller_enqueue(controller, view)); + } + + return {}; +} + +// https://streams.spec.whatwg.org/#readablestream-enqueue +WebIDL::ExceptionOr ReadableStream::enqueue(JS::Value chunk) +{ + VERIFY(m_controller.has_value()); + + // 1. If stream.[[controller]] implements ReadableStreamDefaultController, + if (m_controller->has>()) { + // 1. Perform ! ReadableStreamDefaultControllerEnqueue(stream.[[controller]], chunk). + return readable_stream_default_controller_enqueue(m_controller->get>(), chunk); + } + // 2. Otherwise, + else { + // 1. Assert: stream.[[controller]] implements ReadableByteStreamController. + VERIFY(m_controller->has>()); + auto readable_byte_controller = m_controller->get>(); + + // FIXME: 2. Assert: chunk is an ArrayBufferView. + + // 3. Let byobView be the current BYOB request view for stream. + // FIXME: This is not what the spec means by 'current BYOB request view' + auto byob_view = readable_byte_controller->raw_byob_request(); + + // 4. If byobView is non-null, and chunk.[[ViewedArrayBuffer]] is byobView.[[ViewedArrayBuffer]], then: + if (byob_view) { + // FIXME: 1. Assert: chunk.[[ByteOffset]] is byobView.[[ByteOffset]]. + // FIXME: 2. Assert: chunk.[[ByteLength]] ≤ byobView.[[ByteLength]]. + // FIXME: 3. Perform ? ReadableByteStreamControllerRespond(stream.[[controller]], chunk.[[ByteLength]]). + TODO(); + } + + // 5. Otherwise, perform ? ReadableByteStreamControllerEnqueue(stream.[[controller]], chunk). + return readable_byte_stream_controller_enqueue(readable_byte_controller, chunk); + } +} + +// https://streams.spec.whatwg.org/#readablestream-set-up-with-byte-reading-support +void ReadableStream::set_up_with_byte_reading_support(GC::Ptr pull_algorithm, GC::Ptr cancel_algorithm, double high_water_mark) +{ + auto& realm = this->realm(); + + // 1. Let startAlgorithm be an algorithm that returns undefined. + auto start_algorithm = GC::create_function(realm.heap(), []() -> WebIDL::ExceptionOr { return JS::js_undefined(); }); + + // 2. Let pullAlgorithmWrapper be an algorithm that runs these steps: + auto pull_algorithm_wrapper = GC::create_function(realm.heap(), [&realm, pull_algorithm]() { + // 1. Let result be the result of running pullAlgorithm, if pullAlgorithm was given, or null otherwise. If this throws an exception e, return a promise rejected with e. + GC::Ptr result = nullptr; + if (pull_algorithm) + result = pull_algorithm->function()(); + + // 2. If result is a Promise, then return result. + if (result != nullptr) + return GC::Ref(*result); + + // 3. Return a promise resolved with undefined. + return WebIDL::create_resolved_promise(realm, JS::js_undefined()); + }); + + // 3. Let cancelAlgorithmWrapper be an algorithm that runs these steps: + auto cancel_algorithm_wrapper = GC::create_function(realm.heap(), [&realm, cancel_algorithm](JS::Value c) { + // 1. Let result be the result of running cancelAlgorithm, if cancelAlgorithm was given, or null otherwise. If this throws an exception e, return a promise rejected with e. + GC::Ptr result = nullptr; + if (cancel_algorithm) + result = cancel_algorithm->function()(c); + + // 2. If result is a Promise, then return result. + if (result != nullptr) + return GC::Ref(*result); + + // 3. Return a promise resolved with undefined. + return WebIDL::create_resolved_promise(realm, JS::js_undefined()); + }); + + // 4. Perform ! InitializeReadableStream(stream). + // 5. Let controller be a new ReadableByteStreamController. + auto controller = realm.create(realm); + + // 6. Perform ! SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithmWrapper, cancelAlgorithmWrapper, highWaterMark, undefined). + MUST(set_up_readable_byte_stream_controller(*this, controller, start_algorithm, pull_algorithm_wrapper, cancel_algorithm_wrapper, high_water_mark, JS::js_undefined())); +} + } diff --git a/Libraries/LibWeb/Streams/ReadableStream.h b/Libraries/LibWeb/Streams/ReadableStream.h index e260ef31393..29d3418cc30 100644 --- a/Libraries/LibWeb/Streams/ReadableStream.h +++ b/Libraries/LibWeb/Streams/ReadableStream.h @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace Web::Streams { @@ -104,6 +105,10 @@ public: State state() const { return m_state; } void set_state(State value) { m_state = value; } + WebIDL::ExceptionOr pull_from_bytes(ByteBuffer); + WebIDL::ExceptionOr enqueue(JS::Value chunk); + void set_up_with_byte_reading_support(GC::Ptr = {}, GC::Ptr = {}, double high_water_mark = 0); + private: explicit ReadableStream(JS::Realm&);