mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-11 13:36:13 +03:00
LibWeb: Implement AO readable_stream_from_iterable
This commit is contained in:
parent
ce521a196d
commit
01a8b5ee54
Notes:
sideshowbarker
2024-07-17 11:34:34 +09:00
Author: https://github.com/kennethmyhra Commit: https://github.com/LadybirdBrowser/ladybird/commit/01a8b5ee54 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/108 Reviewed-by: https://github.com/shannonbooth ✅
@ -12,6 +12,7 @@
|
||||
#include <LibJS/Runtime/DataView.h>
|
||||
#include <LibJS/Runtime/DataViewConstructor.h>
|
||||
#include <LibJS/Runtime/Intrinsics.h>
|
||||
#include <LibJS/Runtime/Iterator.h>
|
||||
#include <LibJS/Runtime/PromiseCapability.h>
|
||||
#include <LibJS/Runtime/PromiseConstructor.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
@ -1383,6 +1384,116 @@ void readable_stream_error(ReadableStream& stream, JS::Value error)
|
||||
}
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#readable-stream-from-iterable
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> readable_stream_from_iterable(JS::VM& vm, JS::Value async_iterable)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Let stream be undefined.
|
||||
// NON-STANDARD: We capture 'stream' in a lambda later, so it needs to be allocated now.
|
||||
// 'stream' is still in an uninitialized state and will be initialized / set up at step 6.
|
||||
auto stream = realm.heap().allocate<ReadableStream>(realm, realm);
|
||||
|
||||
// 2. Let iteratorRecord be ? GetIterator(asyncIterable, async).
|
||||
auto iterator_record = TRY(JS::get_iterator(vm, async_iterable, JS::IteratorHint::Async));
|
||||
|
||||
// 3. Let startAlgorithm be an algorithm that returns undefined.
|
||||
auto start_algorithm = JS::create_heap_function(realm.heap(), []() -> WebIDL::ExceptionOr<JS::Value> {
|
||||
return JS::js_undefined();
|
||||
});
|
||||
|
||||
// 4. Let pullAlgorithm be the following steps:
|
||||
auto pull_algorithm = JS::create_heap_function(realm.heap(), [&vm, &realm, stream, iterator_record]() mutable {
|
||||
// 1. Let nextResult be IteratorNext(iteratorRecord).
|
||||
auto next_result = JS::iterator_next(vm, iterator_record);
|
||||
|
||||
// 2. If nextResult is an abrupt completion, return a promise rejected with nextResult.[[Value]].
|
||||
if (next_result.is_error())
|
||||
return WebIDL::create_rejected_promise(realm, *next_result.throw_completion().release_value());
|
||||
|
||||
// 3. Let nextPromise be a promise resolved with nextResult.[[Value]].
|
||||
auto next_promise = WebIDL::create_resolved_promise(realm, next_result.release_value());
|
||||
|
||||
// 4. Return the result of reacting to nextPromise with the following fulfillment steps, given iterResult:
|
||||
auto react_result = WebIDL::react_to_promise(*next_promise,
|
||||
JS::create_heap_function(realm.heap(), [&vm, stream](JS::Value iter_result) -> WebIDL::ExceptionOr<JS::Value> {
|
||||
// 1. If Type(iterResult) is not Object, throw a TypeError.
|
||||
if (!iter_result.is_object())
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "iterResult is not an Object"sv };
|
||||
|
||||
// 2. Let done be ? IteratorComplete(iterResult).
|
||||
auto done = TRY(JS::iterator_complete(vm, iter_result.as_object()));
|
||||
|
||||
// 3. If done is true:
|
||||
if (done) {
|
||||
// 1. Perform ! ReadableStreamDefaultControllerClose(stream.[[controller]]).
|
||||
readable_stream_default_controller_close(*stream->controller()->get<JS::NonnullGCPtr<ReadableStreamDefaultController>>());
|
||||
}
|
||||
// 4. Otherwise:
|
||||
else {
|
||||
// 1. Let value be ? IteratorValue(iterResult).
|
||||
auto value = TRY(JS::iterator_value(vm, iter_result.as_object()));
|
||||
|
||||
// 2. Perform ! ReadableStreamDefaultControllerEnqueue(stream.[[controller]], value).
|
||||
MUST(readable_stream_default_controller_enqueue(*stream->controller()->get<JS::NonnullGCPtr<ReadableStreamDefaultController>>(), value));
|
||||
}
|
||||
|
||||
return JS::js_undefined();
|
||||
}),
|
||||
{});
|
||||
|
||||
return WebIDL::create_resolved_promise(realm, react_result);
|
||||
});
|
||||
|
||||
// 5. Let cancelAlgorithm be the following steps, given reason:
|
||||
auto cancel_algorithm = JS::create_heap_function(realm.heap(), [&vm, &realm, iterator_record](JS::Value reason) {
|
||||
// 1. Let iterator be iteratorRecord.[[Iterator]].
|
||||
auto iterator = iterator_record->iterator;
|
||||
|
||||
// 2. Let returnMethod be GetMethod(iterator, "return").
|
||||
auto return_method = iterator->get(vm.names.return_);
|
||||
|
||||
// 3. If returnMethod is an abrupt completion, return a promise rejected with returnMethod.[[Value]].
|
||||
if (return_method.is_error())
|
||||
return WebIDL::create_rejected_promise(realm, *return_method.throw_completion().release_value());
|
||||
|
||||
// 4. If returnMethod.[[Value]] is undefined, return a promise resolved with undefined.
|
||||
if (return_method.value().is_undefined())
|
||||
return WebIDL::create_resolved_promise(realm, JS::js_undefined());
|
||||
|
||||
// 5. Let returnResult be Call(returnMethod.[[Value]], iterator, « reason »).
|
||||
auto return_result = JS::call(vm, return_method.value(), reason);
|
||||
|
||||
// 6. If returnResult is an abrupt completion, return a promise rejected with returnResult.[[Value]].
|
||||
if (return_result.is_error())
|
||||
return WebIDL::create_rejected_promise(realm, *return_result.throw_completion().release_value());
|
||||
|
||||
// 7. Let returnPromise be a promise resolved with returnResult.[[Value]].
|
||||
auto return_promise = WebIDL::create_resolved_promise(realm, return_result.release_value());
|
||||
|
||||
// 8. Return the result of reacting to returnPromise with the following fulfillment steps, given iterResult:
|
||||
auto react_result = WebIDL::react_to_promise(*return_promise,
|
||||
JS::create_heap_function(realm.heap(), [](JS::Value iter_result) -> WebIDL::ExceptionOr<JS::Value> {
|
||||
// 1. If Type(iterResult) is not Object, throw a TypeError.
|
||||
if (!iter_result.is_object())
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "iterResult is not an Object"sv };
|
||||
|
||||
// 2. Return undefined.
|
||||
return JS::js_undefined();
|
||||
}),
|
||||
{});
|
||||
|
||||
return WebIDL::create_resolved_promise(realm, react_result);
|
||||
});
|
||||
|
||||
// 6. Set stream to ! CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, 0).
|
||||
// NON-STANDARD: 'stream' is captured in a lambda defined earlier, so we cannot overwrite it by assigning the ReadableStream returned by CreateReadableStream.
|
||||
MUST(set_up_readable_stream(realm, *stream, start_algorithm, pull_algorithm, cancel_algorithm, 0));
|
||||
|
||||
// 7. Return stream.
|
||||
return stream;
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#readable-stream-add-read-request
|
||||
void readable_stream_add_read_request(ReadableStream& stream, JS::NonnullGCPtr<ReadRequest> read_request)
|
||||
{
|
||||
|
@ -37,6 +37,7 @@ WebIDL::ExceptionOr<double> extract_high_water_mark(QueuingStrategy const&, doub
|
||||
|
||||
void readable_stream_close(ReadableStream&);
|
||||
void readable_stream_error(ReadableStream&, JS::Value error);
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<ReadableStream>> readable_stream_from_iterable(JS::VM& vm, JS::Value async_iterable);
|
||||
void readable_stream_add_read_request(ReadableStream&, JS::NonnullGCPtr<ReadRequest>);
|
||||
void readable_stream_add_read_into_request(ReadableStream&, JS::NonnullGCPtr<ReadIntoRequest>);
|
||||
JS::NonnullGCPtr<WebIDL::Promise> readable_stream_cancel(ReadableStream&, JS::Value reason);
|
||||
|
Loading…
Reference in New Issue
Block a user