From d3b2cd8ab6fa7f7d690f172cae7466590adaf285 Mon Sep 17 00:00:00 2001 From: Kenneth Myhra Date: Sat, 6 Apr 2024 19:02:28 +0200 Subject: [PATCH] LibWeb: Implement ReadableStream.pipeTo() --- .../LibWeb/Streams/ReadableStream.cpp | 27 +++++++++++++++++++ .../Libraries/LibWeb/Streams/ReadableStream.h | 9 +++++++ .../LibWeb/Streams/ReadableStream.idl | 11 +++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStream.cpp b/Userland/Libraries/LibWeb/Streams/ReadableStream.cpp index bd347de7708..daa506c4005 100644 --- a/Userland/Libraries/LibWeb/Streams/ReadableStream.cpp +++ b/Userland/Libraries/LibWeb/Streams/ReadableStream.cpp @@ -1,12 +1,14 @@ /* * Copyright (c) 2022, Linus Groh * Copyright (c) 2023-2024, Shannon Booth + * Copyright (c) 2024, Kenneth Myhra * * SPDX-License-Identifier: BSD-2-Clause */ #include #include +#include #include #include #include @@ -108,6 +110,31 @@ WebIDL::ExceptionOr ReadableStream::get_reader(ReadableStr return ReadableStreamReader { TRY(acquire_readable_stream_byob_reader(*this)) }; } +WebIDL::ExceptionOr> ReadableStream::pipe_to(WritableStream& destination, StreamPipeOptions const& options) +{ + auto& realm = this->realm(); + + // 1. If ! IsReadableStreamLocked(this) is true, return a promise rejected with a TypeError exception. + if (is_readable_stream_locked(*this)) { + auto promise = WebIDL::create_promise(realm); + WebIDL::reject_promise(realm, promise, JS::TypeError::create(realm, "Failed to execute 'pipeTo' on 'ReadableStream': Cannot pipe a locked stream"sv)); + return promise->promise(); + } + + // 2. If ! IsWritableStreamLocked(destination) is true, return a promise rejected with a TypeError exception. + if (is_writable_stream_locked(destination)) { + auto promise = WebIDL::create_promise(realm); + WebIDL::reject_promise(realm, promise, JS::TypeError::create(realm, "Failed to execute 'pipeTo' on 'ReadableStream': Cannot pipe to a locked stream"sv)); + return promise->promise(); + } + + // 3. Let signal be options["signal"] if it exists, or undefined otherwise. + auto signal = options.signal.has_value() ? JS::Value(options.signal.value().ptr()) : JS::js_undefined(); + + // 4. Return ! ReadableStreamPipeTo(this, destination, options["preventClose"], options["preventAbort"], options["preventCancel"], signal). + return MUST(readable_stream_pipe_to(*this, destination, options.prevent_close, options.prevent_abort, options.prevent_cancel, signal))->promise(); +} + // https://streams.spec.whatwg.org/#readablestream-tee WebIDL::ExceptionOr ReadableStream::tee() { diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStream.h b/Userland/Libraries/LibWeb/Streams/ReadableStream.h index e9459fb9243..18ac8ac6575 100644 --- a/Userland/Libraries/LibWeb/Streams/ReadableStream.h +++ b/Userland/Libraries/LibWeb/Streams/ReadableStream.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2022, Linus Groh + * Copyright (c) 2024, Kenneth Myhra * * SPDX-License-Identifier: BSD-2-Clause */ @@ -26,6 +27,13 @@ struct ReadableStreamGetReaderOptions { Optional mode; }; +struct StreamPipeOptions { + bool prevent_close { false }; + bool prevent_abort { false }; + bool prevent_cancel { false }; + Optional> signal; +}; + struct ReadableStreamPair { // Define a couple container-like methods so this type may be used as the return type of the IDL `tee` implementation. size_t size() const { return 2; } @@ -62,6 +70,7 @@ public: bool locked() const; WebIDL::ExceptionOr> cancel(JS::Value reason); WebIDL::ExceptionOr get_reader(ReadableStreamGetReaderOptions const& = {}); + WebIDL::ExceptionOr> pipe_to(WritableStream& destination, StreamPipeOptions const& = {}); WebIDL::ExceptionOr tee(); Optional& controller() { return m_controller; } diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStream.idl b/Userland/Libraries/LibWeb/Streams/ReadableStream.idl index cedf67892db..418bc65481a 100644 --- a/Userland/Libraries/LibWeb/Streams/ReadableStream.idl +++ b/Userland/Libraries/LibWeb/Streams/ReadableStream.idl @@ -1,6 +1,15 @@ +#import #import #import #import +#import + +dictionary StreamPipeOptions { + boolean preventClose = false; + boolean preventAbort = false; + boolean preventCancel = false; + AbortSignal signal; +}; // https://streams.spec.whatwg.org/#enumdef-readablestreamreadermode enum ReadableStreamReaderMode { "byob" }; @@ -22,7 +31,7 @@ interface ReadableStream { Promise cancel(optional any reason); ReadableStreamReader getReader(optional ReadableStreamGetReaderOptions options = {}); // FIXME: ReadableStream pipeThrough(ReadableWritablePair transform, optional StreamPipeOptions options = {}); - // FIXME: Promise pipeTo(WritableStream destination, optional StreamPipeOptions options = {}); + Promise pipeTo(WritableStream destination, optional StreamPipeOptions options = {}); sequence tee(); // FIXME: async iterable(optional ReadableStreamIteratorOptions options = {});