diff options
Diffstat (limited to 'Userland')
5 files changed, 160 insertions, 3 deletions
diff --git a/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp b/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp index ac51a21aed..8866a58be2 100644 --- a/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp +++ b/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp @@ -13,6 +13,8 @@ #include <LibWeb/Streams/ReadableStreamDefaultController.h> #include <LibWeb/Streams/ReadableStreamDefaultReader.h> #include <LibWeb/Streams/ReadableStreamGenericReader.h> +#include <LibWeb/Streams/UnderlyingSource.h> +#include <LibWeb/WebIDL/AbstractOperations.h> #include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/Promise.h> @@ -601,4 +603,120 @@ bool readable_stream_default_controller_can_close_or_enqueue(ReadableStreamDefau return !controller.close_requested() && controller.stream()->is_readable(); } +// https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller +WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller(ReadableStream& stream, ReadableStreamDefaultController& controller, StartAlgorithm&& start_algorithm, PullAlgorithm&& pull_algorithm, CancelAlgorithm&& cancel_algorithm, double high_water_mark, SizeAlgorithm&& size_algorithm) +{ + auto& realm = stream.realm(); + + // 1. Assert: stream.[[controller]] is undefined. + VERIFY(!stream.controller()); + + // 2. Set controller.[[stream]] to stream. + controller.set_stream(stream); + + // 3. Perform ! ResetQueue(controller). + reset_queue(controller); + + // 4. Set controller.[[started]], controller.[[closeRequested]], controller.[[pullAgain]], and controller.[[pulling]] to false. + controller.set_started(false); + controller.set_close_requested(false); + controller.set_pull_again(false); + controller.set_pulling(false); + + // 5. Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm and controller.[[strategyHWM]] to highWaterMark. + controller.set_strategy_size_algorithm(move(size_algorithm)); + controller.set_strategy_hwm(high_water_mark); + + // 6. Set controller.[[pullAlgorithm]] to pullAlgorithm. + controller.set_pull_algorithm(move(pull_algorithm)); + + // 7. Set controller.[[cancelAlgorithm]] to cancelAlgorithm. + controller.set_cancel_algorithm(move(cancel_algorithm)); + + // 8. Set stream.[[controller]] to controller. + stream.set_controller(controller); + + // 9. Let startResult be the result of performing startAlgorithm. (This might throw an exception.) + auto start_result = TRY(start_algorithm()); + + // 10. Let startPromise be a promise resolved with startResult. + auto start_promise = WebIDL::create_resolved_promise(realm, start_result ? start_result->promise() : JS::js_undefined()); + + // 11. Upon fulfillment of startPromise, + WebIDL::upon_fulfillment(start_promise, [&](auto const&) -> WebIDL::ExceptionOr<JS::Value> { + // 1. Set controller.[[started]] to true. + controller.set_started(true); + + // 2. Assert: controller.[[pulling]] is false. + VERIFY(!controller.pulling()); + + // 3. Assert: controller.[[pullAgain]] is false. + VERIFY(!controller.pull_again()); + + // 4. Perform ! ReadableStreamDefaultControllerCallPullIfNeeded(controller). + TRY(readable_stream_default_controller_can_pull_if_needed(controller)); + + return JS::js_undefined(); + }); + + // 12. Upon rejection of startPromise with reason r, + WebIDL::upon_rejection(start_promise, [&](auto const& r) -> WebIDL::ExceptionOr<JS::Value> { + // 1. Perform ! ReadableStreamDefaultControllerError(controller, r). + readable_stream_default_controller_error(controller, r); + + return JS::js_undefined(); + }); + + return {}; +} + +// https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller-from-underlying-source +WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underlying_source(ReadableStream& stream, JS::Value underlying_source_value, UnderlyingSource underlying_source, double high_water_mark, SizeAlgorithm&& size_algorithm) +{ + auto& realm = stream.realm(); + + // 1. Let controller be a new ReadableStreamDefaultController. + auto controller = MUST_OR_THROW_OOM(stream.heap().allocate<ReadableStreamDefaultController>(realm, realm)); + + // 2. Let startAlgorithm be an algorithm that returns undefined. + StartAlgorithm start_algorithm = [] { return JS::GCPtr<WebIDL::Promise> {}; }; + + // 3. Let pullAlgorithm be an algorithm that returns a promise resolved with undefined. + PullAlgorithm pull_algorithm = [&realm]() { + return WebIDL::create_resolved_promise(realm, JS::js_undefined()); + }; + + // 4. Let cancelAlgorithm be an algorithm that returns a promise resolved with undefined. + CancelAlgorithm cancel_algorithm = [&realm](auto const&) { + return WebIDL::create_resolved_promise(realm, JS::js_undefined()); + }; + + // 5. If underlyingSourceDict["start"] exists, then set startAlgorithm to an algorithm which returns the result of invoking underlyingSourceDict["start"] with argument list « controller » and callback this value underlyingSource. + if (underlying_source.start) { + start_algorithm = [&, start = underlying_source.start]() -> WebIDL::ExceptionOr<JS::GCPtr<WebIDL::Promise>> { + auto result = TRY(WebIDL::invoke_callback(*start, underlying_source_value, controller)).release_value(); + return WebIDL::create_resolved_promise(realm, result); + }; + } + + // 6. If underlyingSourceDict["pull"] exists, then set pullAlgorithm to an algorithm which returns the result of invoking underlyingSourceDict["pull"] with argument list « controller » and callback this value underlyingSource. + if (underlying_source.pull) { + pull_algorithm = [&, pull = underlying_source.pull]() -> WebIDL::ExceptionOr<JS::GCPtr<WebIDL::Promise>> { + auto result = TRY(WebIDL::invoke_callback(*pull, underlying_source_value, controller)).release_value(); + return WebIDL::create_resolved_promise(realm, result); + }; + } + + // 7. If underlyingSourceDict["cancel"] exists, then set cancelAlgorithm to an algorithm which takes an argument reason and returns the result of invoking underlyingSourceDict["cancel"] with argument list « reason » and callback this value underlyingSource. + if (underlying_source.cancel) { + cancel_algorithm = [&, cancel = underlying_source.cancel](auto const& reason) -> WebIDL::ExceptionOr<JS::GCPtr<WebIDL::Promise>> { + auto result = TRY(WebIDL::invoke_callback(*cancel, underlying_source_value, reason)).release_value(); + return WebIDL::create_resolved_promise(realm, result); + }; + } + + // 8. Perform ? SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm). + return set_up_readable_stream_default_controller(stream, controller, move(start_algorithm), move(pull_algorithm), move(cancel_algorithm), high_water_mark, move(size_algorithm)); +} + } diff --git a/Userland/Libraries/LibWeb/Streams/AbstractOperations.h b/Userland/Libraries/LibWeb/Streams/AbstractOperations.h index 33dea67b50..bb67fa4043 100644 --- a/Userland/Libraries/LibWeb/Streams/AbstractOperations.h +++ b/Userland/Libraries/LibWeb/Streams/AbstractOperations.h @@ -17,6 +17,7 @@ namespace Web::Streams { using SizeAlgorithm = JS::SafeFunction<JS::Completion(JS::Value)>; using PullAlgorithm = JS::SafeFunction<WebIDL::ExceptionOr<JS::GCPtr<WebIDL::Promise>>()>; using CancelAlgorithm = JS::SafeFunction<WebIDL::ExceptionOr<JS::GCPtr<WebIDL::Promise>>(JS::Value)>; +using StartAlgorithm = JS::SafeFunction<WebIDL::ExceptionOr<JS::GCPtr<WebIDL::Promise>>()>; bool is_readable_stream_locked(ReadableStream const&); @@ -45,5 +46,7 @@ void readable_stream_default_controller_clear_algorithms(ReadableStreamDefaultCo void readable_stream_default_controller_error(ReadableStreamDefaultController&, JS::Value error); Optional<float> readable_stream_default_controller_get_desired_size(ReadableStreamDefaultController&); bool readable_stream_default_controller_can_close_or_enqueue(ReadableStreamDefaultController&); +WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller(ReadableStream&, ReadableStreamDefaultController&, StartAlgorithm&&, PullAlgorithm&&, CancelAlgorithm&&, double high_water_mark, SizeAlgorithm&&); +WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underlying_source(ReadableStream&, JS::Value underlying_source_value, UnderlyingSource, double high_water_mark, SizeAlgorithm&&); } diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStream.cpp b/Userland/Libraries/LibWeb/Streams/ReadableStream.cpp index 8eda868bee..092ff2e4dd 100644 --- a/Userland/Libraries/LibWeb/Streams/ReadableStream.cpp +++ b/Userland/Libraries/LibWeb/Streams/ReadableStream.cpp @@ -9,14 +9,48 @@ #include <LibWeb/Streams/ReadableStream.h> #include <LibWeb/Streams/ReadableStreamDefaultController.h> #include <LibWeb/Streams/ReadableStreamDefaultReader.h> +#include <LibWeb/Streams/UnderlyingSource.h> #include <LibWeb/WebIDL/ExceptionOr.h> namespace Web::Streams { // https://streams.spec.whatwg.org/#rs-constructor -WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> ReadableStream::construct_impl(JS::Realm& realm) +WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> ReadableStream::construct_impl(JS::Realm& realm, Optional<JS::Handle<JS::Object>> const& underlying_source_object) { - return MUST_OR_THROW_OOM(realm.heap().allocate<ReadableStream>(realm, realm)); + auto readable_stream = MUST_OR_THROW_OOM(realm.heap().allocate<ReadableStream>(realm, realm)); + + // 1. If underlyingSource is missing, set it to null. + auto underlying_source = underlying_source_object.has_value() ? JS::Value(underlying_source_object.value().ptr()) : JS::js_null(); + + // 2. Let underlyingSourceDict be underlyingSource, converted to an IDL value of type UnderlyingSource. + auto underlying_source_dict = TRY(UnderlyingSource::from_value(realm.vm(), underlying_source)); + + // 3. Perform ! InitializeReadableStream(this). + + // 4. If underlyingSourceDict["type"] is "bytes": + if (underlying_source_dict.type.has_value() && underlying_source_dict.type.value() == ReadableStreamType::Bytes) { + // FIXME: + // 1. If strategy["size"] exists, throw a RangeError exception. + // 2. Let highWaterMark be ? ExtractHighWaterMark(strategy, 0). + // 3. Perform ? SetUpReadableByteStreamControllerFromUnderlyingSource(this, underlyingSource, underlyingSourceDict, highWaterMark). + TODO(); + } + // 5. Otherwise, + else { + // 1. Assert: underlyingSourceDict["type"] does not exist. + VERIFY(!underlying_source_dict.type.has_value()); + + // FIXME: 2. Let sizeAlgorithm be ! ExtractSizeAlgorithm(strategy). + SizeAlgorithm size_algorithm = [](auto const&) { return JS::normal_completion(JS::Value(1)); }; + + // FIXME: 3. Let highWaterMark be ? ExtractHighWaterMark(strategy, 1). + auto high_water_mark = 1.0; + + // 4. Perform ? SetUpReadableStreamDefaultControllerFromUnderlyingSource(this, underlyingSource, underlyingSourceDict, highWaterMark, sizeAlgorithm). + TRY(set_up_readable_stream_default_controller_from_underlying_source(*readable_stream, underlying_source, underlying_source_dict, high_water_mark, move(size_algorithm))); + } + + return readable_stream; } ReadableStream::ReadableStream(JS::Realm& realm) diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStream.h b/Userland/Libraries/LibWeb/Streams/ReadableStream.h index 790a1ac78d..c12592142a 100644 --- a/Userland/Libraries/LibWeb/Streams/ReadableStream.h +++ b/Userland/Libraries/LibWeb/Streams/ReadableStream.h @@ -32,7 +32,7 @@ public: Errored, }; - static WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> construct_impl(JS::Realm&); + static WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> construct_impl(JS::Realm&, Optional<JS::Handle<JS::Object>> const& underlying_source); virtual ~ReadableStream() override; diff --git a/Userland/Libraries/LibWeb/Streams/ReadableStream.idl b/Userland/Libraries/LibWeb/Streams/ReadableStream.idl index 4d18a06b7c..0cdc9aade6 100644 --- a/Userland/Libraries/LibWeb/Streams/ReadableStream.idl +++ b/Userland/Libraries/LibWeb/Streams/ReadableStream.idl @@ -3,4 +3,6 @@ // https://streams.spec.whatwg.org/#readablestream [Exposed=*, Transferable] interface ReadableStream { + // FIXME: optional QueuingStrategy strategy = {} + constructor(optional object underlyingSource); }; |