mirror of
				https://github.com/LadybirdBrowser/ladybird.git
				synced 2025-11-04 07:10:57 +00:00 
			
		
		
		
	The main streams AO file has gotten very large, and is a bit difficult to navigate. In an effort to improve DX, this migrates WritableStream AOs to their own file.
		
			
				
	
	
		
			1293 lines
		
	
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1293 lines
		
	
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						||
 * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
 | 
						||
 * Copyright (c) 2023, Matthew Olsson <mattco@serenityos.org>
 | 
						||
 * Copyright (c) 2023-2025, Shannon Booth <shannon@serenityos.org>
 | 
						||
 * Copyright (c) 2023-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
 | 
						||
 * Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
 | 
						||
 *
 | 
						||
 * SPDX-License-Identifier: BSD-2-Clause
 | 
						||
 */
 | 
						||
 | 
						||
#include <AK/GenericShorthands.h>
 | 
						||
#include <LibWeb/Bindings/ExceptionOrUtils.h>
 | 
						||
#include <LibWeb/DOM/AbortSignal.h>
 | 
						||
#include <LibWeb/Streams/AbstractOperations.h>
 | 
						||
#include <LibWeb/Streams/UnderlyingSink.h>
 | 
						||
#include <LibWeb/Streams/WritableStream.h>
 | 
						||
#include <LibWeb/Streams/WritableStreamDefaultController.h>
 | 
						||
#include <LibWeb/Streams/WritableStreamDefaultWriter.h>
 | 
						||
#include <LibWeb/Streams/WritableStreamOperations.h>
 | 
						||
#include <LibWeb/WebIDL/AbstractOperations.h>
 | 
						||
#include <LibWeb/WebIDL/Promise.h>
 | 
						||
 | 
						||
namespace Web::Streams {
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#close-sentinel
 | 
						||
static JS::Value create_close_sentinel()
 | 
						||
{
 | 
						||
    // The close sentinel is a unique value enqueued into [[queue]], in lieu of a chunk, to signal that the stream is
 | 
						||
    // closed. It is only used internally, and is never exposed to web developers.
 | 
						||
    return JS::js_special_empty_value();
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#close-sentinel
 | 
						||
static bool is_close_sentinel(JS::Value value)
 | 
						||
{
 | 
						||
    return value.is_special_empty_value();
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#acquire-writable-stream-default-writer
 | 
						||
WebIDL::ExceptionOr<GC::Ref<WritableStreamDefaultWriter>> acquire_writable_stream_default_writer(WritableStream& stream)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. Let writer be a new WritableStreamDefaultWriter.
 | 
						||
    auto writer = realm.create<WritableStreamDefaultWriter>(realm);
 | 
						||
 | 
						||
    // 2. Perform ? SetUpWritableStreamDefaultWriter(writer, stream).
 | 
						||
    TRY(set_up_writable_stream_default_writer(writer, stream));
 | 
						||
 | 
						||
    // 3. Return writer.
 | 
						||
    return writer;
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#create-writable-stream
 | 
						||
WebIDL::ExceptionOr<GC::Ref<WritableStream>> create_writable_stream(JS::Realm& realm, GC::Ref<StartAlgorithm> start_algorithm, GC::Ref<WriteAlgorithm> write_algorithm, GC::Ref<CloseAlgorithm> close_algorithm, GC::Ref<AbortAlgorithm> abort_algorithm, double high_water_mark, GC::Ref<SizeAlgorithm> size_algorithm)
 | 
						||
{
 | 
						||
    // 1. Assert: ! IsNonNegativeNumber(highWaterMark) is true.
 | 
						||
    VERIFY(is_non_negative_number(JS::Value { high_water_mark }));
 | 
						||
 | 
						||
    // 2. Let stream be a new WritableStream.
 | 
						||
    auto stream = realm.create<WritableStream>(realm);
 | 
						||
 | 
						||
    // 3. Perform ! InitializeWritableStream(stream).
 | 
						||
    initialize_writable_stream(stream);
 | 
						||
 | 
						||
    // 4. Let controller be a new WritableStreamDefaultController.
 | 
						||
    auto controller = realm.create<WritableStreamDefaultController>(realm);
 | 
						||
 | 
						||
    // 5. Perform ? SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm).
 | 
						||
    TRY(set_up_writable_stream_default_controller(stream, controller, start_algorithm, write_algorithm, close_algorithm, abort_algorithm, high_water_mark, size_algorithm));
 | 
						||
 | 
						||
    // 6. Return stream.
 | 
						||
    return stream;
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#initialize-writable-stream
 | 
						||
void initialize_writable_stream(WritableStream& stream)
 | 
						||
{
 | 
						||
    // 1. Set stream.[[state]] to "writable".
 | 
						||
    stream.set_state(WritableStream::State::Writable);
 | 
						||
 | 
						||
    // 2. Set stream.[[storedError]], stream.[[writer]], stream.[[controller]], stream.[[inFlightWriteRequest]],
 | 
						||
    //    stream.[[closeRequest]], stream.[[inFlightCloseRequest]], and stream.[[pendingAbortRequest]] to undefined.
 | 
						||
    stream.set_stored_error(JS::js_undefined());
 | 
						||
    stream.set_writer({});
 | 
						||
    stream.set_controller({});
 | 
						||
    stream.set_in_flight_write_request({});
 | 
						||
    stream.set_close_request({});
 | 
						||
    stream.set_in_flight_close_request({});
 | 
						||
    stream.set_pending_abort_request({});
 | 
						||
 | 
						||
    // 3. Set stream.[[writeRequests]] to a new empty list.
 | 
						||
    stream.write_requests().clear();
 | 
						||
 | 
						||
    // 4. Set stream.[[backpressure]] to false.
 | 
						||
    stream.set_backpressure(false);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#is-writable-stream-locked
 | 
						||
bool is_writable_stream_locked(WritableStream const& stream)
 | 
						||
{
 | 
						||
    // 1. If stream.[[writer]] is undefined, return false.
 | 
						||
    if (!stream.writer())
 | 
						||
        return false;
 | 
						||
 | 
						||
    // 2. Return true.
 | 
						||
    return true;
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#set-up-writable-stream-default-writer
 | 
						||
WebIDL::ExceptionOr<void> set_up_writable_stream_default_writer(WritableStreamDefaultWriter& writer, WritableStream& stream)
 | 
						||
{
 | 
						||
    // FIXME: Exactly when we should effectively be using the relevant realm of `this` is to be clarified by the spec.
 | 
						||
    //        For now, we do so as needed by WPT tests. See: https://github.com/whatwg/streams/issues/1213
 | 
						||
    auto& realm = HTML::relevant_realm(writer);
 | 
						||
 | 
						||
    // 1. If ! IsWritableStreamLocked(stream) is true, throw a TypeError exception.
 | 
						||
    if (is_writable_stream_locked(stream))
 | 
						||
        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Stream is locked"sv };
 | 
						||
 | 
						||
    // 2. Set writer.[[stream]] to stream.
 | 
						||
    writer.set_stream(stream);
 | 
						||
 | 
						||
    // 3. Set stream.[[writer]] to writer.
 | 
						||
    stream.set_writer(writer);
 | 
						||
 | 
						||
    // 4. Let state be stream.[[state]].
 | 
						||
    auto state = stream.state();
 | 
						||
 | 
						||
    // 5. If state is "writable",
 | 
						||
    if (state == WritableStream::State::Writable) {
 | 
						||
        // 1. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and stream.[[backpressure]] is true, set
 | 
						||
        //    writer.[[readyPromise]] to a new promise.
 | 
						||
        if (!writable_stream_close_queued_or_in_flight(stream) && stream.backpressure()) {
 | 
						||
            writer.set_ready_promise(WebIDL::create_promise(realm));
 | 
						||
        }
 | 
						||
        // 2. Otherwise, set writer.[[readyPromise]] to a promise resolved with undefined.
 | 
						||
        else {
 | 
						||
            writer.set_ready_promise(WebIDL::create_resolved_promise(realm, JS::js_undefined()));
 | 
						||
        }
 | 
						||
 | 
						||
        // 3. Set writer.[[closedPromise]] to a new promise.
 | 
						||
        writer.set_closed_promise(WebIDL::create_promise(realm));
 | 
						||
    }
 | 
						||
    // 6. Otherwise, if state is "erroring",
 | 
						||
    else if (state == WritableStream::State::Erroring) {
 | 
						||
        // 1. Set writer.[[readyPromise]] to a promise rejected with stream.[[storedError]].
 | 
						||
        writer.set_ready_promise(WebIDL::create_rejected_promise(realm, stream.stored_error()));
 | 
						||
 | 
						||
        // 2. Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
 | 
						||
        WebIDL::mark_promise_as_handled(*writer.ready_promise());
 | 
						||
 | 
						||
        // 3. Set writer.[[closedPromise]] to a new promise.
 | 
						||
        writer.set_closed_promise(WebIDL::create_promise(realm));
 | 
						||
    }
 | 
						||
    // 7. Otherwise, if state is "closed",
 | 
						||
    else if (state == WritableStream::State::Closed) {
 | 
						||
        // 1. Set writer.[[readyPromise]] to a promise resolved with undefined.
 | 
						||
        writer.set_ready_promise(WebIDL::create_resolved_promise(realm, JS::js_undefined()));
 | 
						||
 | 
						||
        // 2. Set writer.[[closedPromise]] to a promise resolved with undefined.
 | 
						||
        writer.set_closed_promise(WebIDL::create_resolved_promise(realm, JS::js_undefined()));
 | 
						||
    }
 | 
						||
    // 8. Otherwise,
 | 
						||
    else {
 | 
						||
        // 1. Assert: state is "errored".
 | 
						||
        VERIFY(state == WritableStream::State::Errored);
 | 
						||
 | 
						||
        // 2. Let storedError be stream.[[storedError]].
 | 
						||
        auto stored_error = stream.stored_error();
 | 
						||
 | 
						||
        // 3. Set writer.[[readyPromise]] to a promise rejected with storedError.
 | 
						||
        writer.set_ready_promise(WebIDL::create_rejected_promise(realm, stored_error));
 | 
						||
 | 
						||
        // 4. Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
 | 
						||
        WebIDL::mark_promise_as_handled(*writer.ready_promise());
 | 
						||
 | 
						||
        // 5. Set writer.[[closedPromise]] to a promise rejected with storedError.
 | 
						||
        writer.set_closed_promise(WebIDL::create_rejected_promise(realm, stored_error));
 | 
						||
 | 
						||
        // 6. Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
 | 
						||
        WebIDL::mark_promise_as_handled(*writer.closed_promise());
 | 
						||
    }
 | 
						||
 | 
						||
    return {};
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-abort
 | 
						||
GC::Ref<WebIDL::Promise> writable_stream_abort(WritableStream& stream, JS::Value reason)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. If stream.[[state]] is "closed" or "errored", return a promise resolved with undefined.
 | 
						||
    if (first_is_one_of(stream.state(), WritableStream::State::Closed, WritableStream::State::Errored))
 | 
						||
        return WebIDL::create_resolved_promise(realm, JS::js_undefined());
 | 
						||
 | 
						||
    // 2. Signal abort on stream.[[controller]].[[signal]] with reason.
 | 
						||
    stream.controller()->signal()->signal_abort(reason);
 | 
						||
 | 
						||
    // 3. Let state be stream.[[state]].
 | 
						||
    auto state = stream.state();
 | 
						||
 | 
						||
    // 4. If state is "closed" or "errored", return a promise resolved with undefined.
 | 
						||
    if (state == WritableStream::State::Closed || state == WritableStream::State::Errored)
 | 
						||
        return WebIDL::create_resolved_promise(realm, JS::js_undefined());
 | 
						||
 | 
						||
    // 5. If stream.[[pendingAbortRequest]] is not undefined, return stream.[[pendingAbortRequest]]'s promise.
 | 
						||
    if (stream.pending_abort_request().has_value())
 | 
						||
        return stream.pending_abort_request()->promise;
 | 
						||
 | 
						||
    // 6. Assert: state is "writable" or "erroring".
 | 
						||
    VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
 | 
						||
 | 
						||
    // 7. Let wasAlreadyErroring be false.
 | 
						||
    auto was_already_erroring = false;
 | 
						||
 | 
						||
    // 8. If state is "erroring",
 | 
						||
    if (state == WritableStream::State::Erroring) {
 | 
						||
        // 1. Set wasAlreadyErroring to true.
 | 
						||
        was_already_erroring = true;
 | 
						||
 | 
						||
        // 2. Set reason to undefined.
 | 
						||
        reason = JS::js_undefined();
 | 
						||
    }
 | 
						||
 | 
						||
    // 9. Let promise be a new promise.
 | 
						||
    auto promise = WebIDL::create_promise(realm);
 | 
						||
 | 
						||
    // 10. Set stream.[[pendingAbortRequest]] to a new pending abort request whose promise is promise, reason is reason,
 | 
						||
    //     and was already erroring is wasAlreadyErroring.
 | 
						||
    stream.set_pending_abort_request(PendingAbortRequest { promise, reason, was_already_erroring });
 | 
						||
 | 
						||
    // 11. If wasAlreadyErroring is false, perform ! WritableStreamStartErroring(stream, reason).
 | 
						||
    if (!was_already_erroring)
 | 
						||
        writable_stream_start_erroring(stream, reason);
 | 
						||
 | 
						||
    // 12. Return promise.
 | 
						||
    return promise;
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-close
 | 
						||
GC::Ref<WebIDL::Promise> writable_stream_close(WritableStream& stream)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. Let state be stream.[[state]].
 | 
						||
    auto state = stream.state();
 | 
						||
 | 
						||
    // 2. If state is "closed" or "errored", return a promise rejected with a TypeError exception.
 | 
						||
    if (state == WritableStream::State::Closed || state == WritableStream::State::Errored) {
 | 
						||
        auto message = state == WritableStream::State::Closed ? "Cannot close a closed stream"sv : "Cannot close an errored stream"sv;
 | 
						||
        auto exception = JS::TypeError::create(realm, message);
 | 
						||
 | 
						||
        return WebIDL::create_rejected_promise(realm, exception);
 | 
						||
    }
 | 
						||
 | 
						||
    // 3. Assert: state is "writable" or "erroring".
 | 
						||
    VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
 | 
						||
 | 
						||
    // 4. Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false.
 | 
						||
    VERIFY(!writable_stream_close_queued_or_in_flight(stream));
 | 
						||
 | 
						||
    // 5. Let promise be a new promise.
 | 
						||
    auto promise = WebIDL::create_promise(realm);
 | 
						||
 | 
						||
    // 6. Set stream.[[closeRequest]] to promise.
 | 
						||
    stream.set_close_request(promise);
 | 
						||
 | 
						||
    // 7. Let writer be stream.[[writer]].
 | 
						||
    auto writer = stream.writer();
 | 
						||
 | 
						||
    // 8. If writer is not undefined, and stream.[[backpressure]] is true, and state is "writable", resolve
 | 
						||
    //    writer.[[readyPromise]] with undefined.
 | 
						||
    if (writer && stream.backpressure() && state == WritableStream::State::Writable)
 | 
						||
        WebIDL::resolve_promise(realm, *writer->ready_promise(), JS::js_undefined());
 | 
						||
 | 
						||
    // 9. Perform ! WritableStreamDefaultControllerClose(stream.[[controller]]).
 | 
						||
    writable_stream_default_controller_close(*stream.controller());
 | 
						||
 | 
						||
    // 10. Return promise.
 | 
						||
    return promise;
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-add-write-request
 | 
						||
GC::Ref<WebIDL::Promise> writable_stream_add_write_request(WritableStream& stream)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. Assert: ! IsWritableStreamLocked(stream) is true.
 | 
						||
    VERIFY(is_writable_stream_locked(stream));
 | 
						||
 | 
						||
    // 2. Assert: stream.[[state]] is "writable".
 | 
						||
    VERIFY(stream.state() == WritableStream::State::Writable);
 | 
						||
 | 
						||
    // 3. Let promise be a new promise.
 | 
						||
    auto promise = WebIDL::create_promise(realm);
 | 
						||
 | 
						||
    // 4. Append promise to stream.[[writeRequests]].
 | 
						||
    stream.write_requests().append(promise);
 | 
						||
 | 
						||
    // 5. Return promise.
 | 
						||
    return promise;
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-close-queued-or-in-flight
 | 
						||
bool writable_stream_close_queued_or_in_flight(WritableStream const& stream)
 | 
						||
{
 | 
						||
    // 1. If stream.[[closeRequest]] is undefined and stream.[[inFlightCloseRequest]] is undefined, return false.
 | 
						||
    if (!stream.close_request() && !stream.in_flight_close_request())
 | 
						||
        return false;
 | 
						||
 | 
						||
    // 2. Return true.
 | 
						||
    return true;
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-deal-with-rejection
 | 
						||
void writable_stream_deal_with_rejection(WritableStream& stream, JS::Value error)
 | 
						||
{
 | 
						||
    // 1. Let state be stream.[[state]].
 | 
						||
    auto state = stream.state();
 | 
						||
 | 
						||
    // 2. If state is "writable",
 | 
						||
    if (state == WritableStream::State::Writable) {
 | 
						||
        // 1. Perform ! WritableStreamStartErroring(stream, error).
 | 
						||
        writable_stream_start_erroring(stream, error);
 | 
						||
 | 
						||
        // 2. Return.
 | 
						||
        return;
 | 
						||
    }
 | 
						||
 | 
						||
    // 3. Assert: state is "erroring".
 | 
						||
    VERIFY(state == WritableStream::State::Erroring);
 | 
						||
 | 
						||
    // 4. Perform ! WritableStreamFinishErroring(stream).
 | 
						||
    writable_stream_finish_erroring(stream);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-finish-erroring
 | 
						||
void writable_stream_finish_erroring(WritableStream& stream)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. Assert: stream.[[state]] is "erroring".
 | 
						||
    VERIFY(stream.state() == WritableStream::State::Erroring);
 | 
						||
 | 
						||
    // 2. Assert: ! WritableStreamHasOperationMarkedInFlight(stream) is false.
 | 
						||
    VERIFY(!writable_stream_has_operation_marked_in_flight(stream));
 | 
						||
 | 
						||
    // 3. Set stream.[[state]] to "errored".
 | 
						||
    stream.set_state(WritableStream::State::Errored);
 | 
						||
 | 
						||
    // 4. Perform ! stream.[[controller]].[[ErrorSteps]]().
 | 
						||
    stream.controller()->error_steps();
 | 
						||
 | 
						||
    // 5. Let storedError be stream.[[storedError]].
 | 
						||
    auto stored_error = stream.stored_error();
 | 
						||
 | 
						||
    // 6. For each writeRequest of stream.[[writeRequests]]:
 | 
						||
    for (auto write_request : stream.write_requests()) {
 | 
						||
        // 1. Reject writeRequest with storedError.
 | 
						||
        WebIDL::reject_promise(realm, *write_request, stored_error);
 | 
						||
    }
 | 
						||
 | 
						||
    // 7. Set stream.[[writeRequests]] to an empty list.
 | 
						||
    stream.write_requests().clear();
 | 
						||
 | 
						||
    // 8. If stream.[[pendingAbortRequest]] is undefined,
 | 
						||
    if (!stream.pending_abort_request().has_value()) {
 | 
						||
        // 1. Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
 | 
						||
        writable_stream_reject_close_and_closed_promise_if_needed(stream);
 | 
						||
 | 
						||
        // 2. Return.
 | 
						||
        return;
 | 
						||
    }
 | 
						||
 | 
						||
    // 9. Let abortRequest be stream.[[pendingAbortRequest]].
 | 
						||
    // 10. Set stream.[[pendingAbortRequest]] to undefined.
 | 
						||
    auto abort_request = stream.pending_abort_request().release_value();
 | 
						||
 | 
						||
    // 11. If abortRequest’s was already erroring is true,
 | 
						||
    if (abort_request.was_already_erroring) {
 | 
						||
        // 1. Reject abortRequest’s promise with storedError.
 | 
						||
        WebIDL::reject_promise(realm, abort_request.promise, stored_error);
 | 
						||
 | 
						||
        // 2. Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
 | 
						||
        writable_stream_reject_close_and_closed_promise_if_needed(stream);
 | 
						||
 | 
						||
        // 3. Return.
 | 
						||
        return;
 | 
						||
    }
 | 
						||
 | 
						||
    // 12. Let promise be ! stream.[[controller]].[[AbortSteps]](abortRequest’s reason).
 | 
						||
    auto promise = stream.controller()->abort_steps(abort_request.reason);
 | 
						||
 | 
						||
    WebIDL::react_to_promise(promise,
 | 
						||
        // 13. Upon fulfillment of promise,
 | 
						||
        GC::create_function(realm.heap(), [&realm, &stream, abort_promise = abort_request.promise](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
 | 
						||
            // 1. Resolve abortRequest’s promise with undefined.
 | 
						||
            WebIDL::resolve_promise(realm, abort_promise, JS::js_undefined());
 | 
						||
 | 
						||
            // 2. Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
 | 
						||
            writable_stream_reject_close_and_closed_promise_if_needed(stream);
 | 
						||
 | 
						||
            return JS::js_undefined();
 | 
						||
        }),
 | 
						||
 | 
						||
        // 14. Upon rejection of promise with reason reason,
 | 
						||
        GC::create_function(realm.heap(), [&realm, &stream, abort_promise = abort_request.promise](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
 | 
						||
            // 1. Reject abortRequest’s promise with reason.
 | 
						||
            WebIDL::reject_promise(realm, abort_promise, reason);
 | 
						||
 | 
						||
            // 2. Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
 | 
						||
            writable_stream_reject_close_and_closed_promise_if_needed(stream);
 | 
						||
 | 
						||
            return JS::js_undefined();
 | 
						||
        }));
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close
 | 
						||
void writable_stream_finish_in_flight_close(WritableStream& stream)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. Assert: stream.[[inFlightCloseRequest]] is not undefined.
 | 
						||
    VERIFY(stream.in_flight_close_request());
 | 
						||
 | 
						||
    // 2. Resolve stream.[[inFlightCloseRequest]] with undefined.
 | 
						||
    WebIDL::resolve_promise(realm, *stream.in_flight_close_request(), JS::js_undefined());
 | 
						||
 | 
						||
    // 3. Set stream.[[inFlightCloseRequest]] to undefined.
 | 
						||
    stream.set_in_flight_close_request({});
 | 
						||
 | 
						||
    // 4. Let state be stream.[[state]].
 | 
						||
    auto state = stream.state();
 | 
						||
 | 
						||
    // 5. Assert: stream.[[state]] is "writable" or "erroring".
 | 
						||
    VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
 | 
						||
 | 
						||
    // 6. If state is "erroring",
 | 
						||
    if (state == WritableStream::State::Erroring) {
 | 
						||
        // 1. Set stream.[[storedError]] to undefined.
 | 
						||
        stream.set_stored_error(JS::js_undefined());
 | 
						||
 | 
						||
        // 2. If stream.[[pendingAbortRequest]] is not undefined,
 | 
						||
        if (stream.pending_abort_request().has_value()) {
 | 
						||
            // 1. Resolve stream.[[pendingAbortRequest]]'s promise with undefined.
 | 
						||
            // 2. Set stream.[[pendingAbortRequest]] to undefined.
 | 
						||
            WebIDL::resolve_promise(realm, stream.pending_abort_request().release_value().promise, JS::js_undefined());
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    // 7. Set stream.[[state]] to "closed".
 | 
						||
    stream.set_state(WritableStream::State::Closed);
 | 
						||
 | 
						||
    // 8. Let writer be stream.[[writer]].
 | 
						||
    auto writer = stream.writer();
 | 
						||
 | 
						||
    // 9. If writer is not undefined, resolve writer.[[closedPromise]] with undefined.
 | 
						||
    if (writer)
 | 
						||
        WebIDL::resolve_promise(realm, *writer->closed_promise(), JS::js_undefined());
 | 
						||
 | 
						||
    // 10. Assert: stream.[[pendingAbortRequest]] is undefined.
 | 
						||
    VERIFY(!stream.pending_abort_request().has_value());
 | 
						||
 | 
						||
    // 11. Assert: stream.[[storedError]] is undefined.
 | 
						||
    VERIFY(stream.stored_error().is_undefined());
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close-with-error
 | 
						||
void writable_stream_finish_in_flight_close_with_error(WritableStream& stream, JS::Value error)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. Assert: stream.[[inFlightCloseRequest]] is not undefined.
 | 
						||
    VERIFY(stream.in_flight_close_request());
 | 
						||
 | 
						||
    // 2. Reject stream.[[inFlightCloseRequest]] with error.
 | 
						||
    WebIDL::reject_promise(realm, *stream.in_flight_close_request(), error);
 | 
						||
 | 
						||
    // 3. Set stream.[[inFlightCloseRequest]] to undefined.
 | 
						||
    stream.set_in_flight_close_request({});
 | 
						||
 | 
						||
    // 4. Assert: stream.[[state]] is "writable" or "erroring".
 | 
						||
    VERIFY(first_is_one_of(stream.state(), WritableStream::State::Writable, WritableStream::State::Erroring));
 | 
						||
 | 
						||
    // 5. If stream.[[pendingAbortRequest]] is not undefined,
 | 
						||
    if (stream.pending_abort_request().has_value()) {
 | 
						||
        // 1. Reject stream.[[pendingAbortRequest]]'s promise with error.
 | 
						||
        // 2. Set stream.[[pendingAbortRequest]] to undefined.
 | 
						||
        WebIDL::reject_promise(realm, stream.pending_abort_request().release_value().promise, error);
 | 
						||
    }
 | 
						||
 | 
						||
    // 6. Perform ! WritableStreamDealWithRejection(stream, error).
 | 
						||
    writable_stream_deal_with_rejection(stream, error);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write
 | 
						||
void writable_stream_finish_in_flight_write(WritableStream& stream)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. Assert: stream.[[inFlightWriteRequest]] is not undefined.
 | 
						||
    VERIFY(stream.in_flight_write_request());
 | 
						||
 | 
						||
    // 2. Resolve stream.[[inFlightWriteRequest]] with undefined.
 | 
						||
    WebIDL::resolve_promise(realm, *stream.in_flight_write_request(), JS::js_undefined());
 | 
						||
 | 
						||
    // 3. Set stream.[[inFlightWriteRequest]] to undefined.
 | 
						||
    stream.set_in_flight_write_request({});
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write-with-error
 | 
						||
void writable_stream_finish_in_flight_write_with_error(WritableStream& stream, JS::Value error)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. Assert: stream.[[inFlightWriteRequest]] is not undefined.
 | 
						||
    VERIFY(stream.in_flight_write_request());
 | 
						||
 | 
						||
    // 2. Reject stream.[[inFlightWriteRequest]] with error.
 | 
						||
    WebIDL::reject_promise(realm, *stream.in_flight_write_request(), error);
 | 
						||
 | 
						||
    // 3. Set stream.[[inFlightWriteRequest]] to undefined.
 | 
						||
    stream.set_in_flight_write_request({});
 | 
						||
 | 
						||
    // 4. Assert: stream.[[state]] is "writable" or "erroring".
 | 
						||
    VERIFY(first_is_one_of(stream.state(), WritableStream::State::Writable, WritableStream::State::Erroring));
 | 
						||
 | 
						||
    // 5. Perform ! WritableStreamDealWithRejection(stream, error).
 | 
						||
    writable_stream_deal_with_rejection(stream, error);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-has-operation-marked-in-flight
 | 
						||
bool writable_stream_has_operation_marked_in_flight(WritableStream const& stream)
 | 
						||
{
 | 
						||
    // 1. If stream.[[inFlightWriteRequest]] is undefined and stream.[[inFlightCloseRequest]] is undefined, return false.
 | 
						||
    if (!stream.in_flight_write_request() && !stream.in_flight_close_request())
 | 
						||
        return false;
 | 
						||
 | 
						||
    // 2. Return true.
 | 
						||
    return true;
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-mark-close-request-in-flight
 | 
						||
void writable_stream_mark_close_request_in_flight(WritableStream& stream)
 | 
						||
{
 | 
						||
    // 1. Assert: stream.[[inFlightCloseRequest]] is undefined.
 | 
						||
    VERIFY(!stream.in_flight_close_request());
 | 
						||
 | 
						||
    // 2. Assert: stream.[[closeRequest]] is not undefined.
 | 
						||
    VERIFY(stream.close_request());
 | 
						||
 | 
						||
    // 3. Set stream.[[inFlightCloseRequest]] to stream.[[closeRequest]].
 | 
						||
    stream.set_in_flight_close_request(stream.close_request());
 | 
						||
 | 
						||
    // 4. Set stream.[[closeRequest]] to undefined.
 | 
						||
    stream.set_close_request({});
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-mark-first-write-request-in-flight
 | 
						||
void writable_stream_mark_first_write_request_in_flight(WritableStream& stream)
 | 
						||
{
 | 
						||
    // 1. Assert: stream.[[inFlightWriteRequest]] is undefined.
 | 
						||
    VERIFY(!stream.in_flight_write_request());
 | 
						||
 | 
						||
    // 2. Assert: stream.[[writeRequests]] is not empty.
 | 
						||
    VERIFY(!stream.write_requests().is_empty());
 | 
						||
 | 
						||
    // 3. Let writeRequest be stream.[[writeRequests]][0].
 | 
						||
    // 4. Remove writeRequest from stream.[[writeRequests]].
 | 
						||
    auto write_request = stream.write_requests().take_first();
 | 
						||
 | 
						||
    // 5. Set stream.[[inFlightWriteRequest]] to writeRequest.
 | 
						||
    stream.set_in_flight_write_request(write_request);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-reject-close-and-closed-promise-if-needed
 | 
						||
void writable_stream_reject_close_and_closed_promise_if_needed(WritableStream& stream)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. Assert: stream.[[state]] is "errored".
 | 
						||
    VERIFY(stream.state() == WritableStream::State::Errored);
 | 
						||
 | 
						||
    // 2. If stream.[[closeRequest]] is not undefined,
 | 
						||
    if (stream.close_request()) {
 | 
						||
        // 1. Assert: stream.[[inFlightCloseRequest]] is undefined.
 | 
						||
        VERIFY(!stream.in_flight_close_request());
 | 
						||
 | 
						||
        // 2. Reject stream.[[closeRequest]] with stream.[[storedError]].
 | 
						||
        WebIDL::reject_promise(realm, *stream.close_request(), stream.stored_error());
 | 
						||
 | 
						||
        // 3. Set stream.[[closeRequest]] to undefined.
 | 
						||
        stream.set_close_request({});
 | 
						||
    }
 | 
						||
 | 
						||
    // 3. Let writer be stream.[[writer]].
 | 
						||
    auto writer = stream.writer();
 | 
						||
 | 
						||
    // 4. If writer is not undefined,
 | 
						||
    if (writer) {
 | 
						||
        // 1. Reject writer.[[closedPromise]] with stream.[[storedError]].
 | 
						||
        WebIDL::reject_promise(realm, *writer->closed_promise(), stream.stored_error());
 | 
						||
 | 
						||
        // 2. Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
 | 
						||
        WebIDL::mark_promise_as_handled(*writer->closed_promise());
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-start-erroring
 | 
						||
void writable_stream_start_erroring(WritableStream& stream, JS::Value reason)
 | 
						||
{
 | 
						||
    // 1. Assert: stream.[[storedError]] is undefined.
 | 
						||
    VERIFY(stream.stored_error().is_undefined());
 | 
						||
 | 
						||
    // 2. Assert: stream.[[state]] is "writable".
 | 
						||
    VERIFY(stream.state() == WritableStream::State::Writable);
 | 
						||
 | 
						||
    // 3. Let controller be stream.[[controller]].
 | 
						||
    auto controller = stream.controller();
 | 
						||
 | 
						||
    // 4. Assert: controller is not undefined.
 | 
						||
    VERIFY(controller);
 | 
						||
 | 
						||
    // 5. Set stream.[[state]] to "erroring".
 | 
						||
    stream.set_state(WritableStream::State::Erroring);
 | 
						||
 | 
						||
    // 6. Set stream.[[storedError]] to reason.
 | 
						||
    stream.set_stored_error(reason);
 | 
						||
 | 
						||
    // 7. Let writer be stream.[[writer]].
 | 
						||
    auto writer = stream.writer();
 | 
						||
 | 
						||
    // 8. If writer is not undefined, perform ! WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason).
 | 
						||
    if (writer)
 | 
						||
        writable_stream_default_writer_ensure_ready_promise_rejected(*writer, reason);
 | 
						||
 | 
						||
    // 9. If ! WritableStreamHasOperationMarkedInFlight(stream) is false and controller.[[started]] is true,
 | 
						||
    //    perform ! WritableStreamFinishErroring(stream).
 | 
						||
    if (!writable_stream_has_operation_marked_in_flight(stream) && controller->started())
 | 
						||
        writable_stream_finish_erroring(stream);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-update-backpressure
 | 
						||
void writable_stream_update_backpressure(WritableStream& stream, bool backpressure)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. Assert: stream.[[state]] is "writable".
 | 
						||
    VERIFY(stream.state() == WritableStream::State::Writable);
 | 
						||
 | 
						||
    // 2. Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false.
 | 
						||
    VERIFY(!writable_stream_close_queued_or_in_flight(stream));
 | 
						||
 | 
						||
    // 3. Let writer be stream.[[writer]].
 | 
						||
    auto writer = stream.writer();
 | 
						||
 | 
						||
    // 4. If writer is not undefined and backpressure is not stream.[[backpressure]],
 | 
						||
    if (writer && backpressure != stream.backpressure()) {
 | 
						||
        // 1. If backpressure is true, set writer.[[readyPromise]] to a new promise.
 | 
						||
        if (backpressure) {
 | 
						||
            writer->set_ready_promise(WebIDL::create_promise(realm));
 | 
						||
        }
 | 
						||
        // 2. Otherwise,
 | 
						||
        else {
 | 
						||
            // 1. Assert: backpressure is false.
 | 
						||
 | 
						||
            // 2. Resolve writer.[[readyPromise]] with undefined.
 | 
						||
            WebIDL::resolve_promise(realm, *writer->ready_promise(), JS::js_undefined());
 | 
						||
        }
 | 
						||
    }
 | 
						||
 | 
						||
    // 5. Set stream.[[backpressure]] to backpressure.
 | 
						||
    stream.set_backpressure(backpressure);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-writer-abort
 | 
						||
GC::Ref<WebIDL::Promise> writable_stream_default_writer_abort(WritableStreamDefaultWriter& writer, JS::Value reason)
 | 
						||
{
 | 
						||
    // 1. Let stream be writer.[[stream]].
 | 
						||
    auto stream = writer.stream();
 | 
						||
 | 
						||
    // 2. Assert: stream is not undefined.
 | 
						||
    VERIFY(stream);
 | 
						||
 | 
						||
    // 3. Return ! WritableStreamAbort(stream, reason).
 | 
						||
    return writable_stream_abort(*stream, reason);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-writer-close
 | 
						||
GC::Ref<WebIDL::Promise> writable_stream_default_writer_close(WritableStreamDefaultWriter& writer)
 | 
						||
{
 | 
						||
    // 1. Let stream be writer.[[stream]].
 | 
						||
    auto stream = writer.stream();
 | 
						||
 | 
						||
    // 2. Assert: stream is not undefined.
 | 
						||
    VERIFY(stream);
 | 
						||
 | 
						||
    // 3. Return ! WritableStreamClose(stream).
 | 
						||
    return writable_stream_close(*stream);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-writer-close-with-error-propagation
 | 
						||
GC::Ref<WebIDL::Promise> writable_stream_default_writer_close_with_error_propagation(WritableStreamDefaultWriter& writer)
 | 
						||
{
 | 
						||
    auto& realm = writer.realm();
 | 
						||
 | 
						||
    // 1. Let stream be writer.[[stream]].
 | 
						||
    auto stream = writer.stream();
 | 
						||
 | 
						||
    // 2. Assert: stream is not undefined.
 | 
						||
    VERIFY(stream);
 | 
						||
 | 
						||
    // 3. Let state be stream.[[state]].
 | 
						||
    auto state = stream->state();
 | 
						||
 | 
						||
    // 4. If ! WritableStreamCloseQueuedOrInFlight(stream) is true or state is "closed", return a promise resolved with undefined.
 | 
						||
    if (writable_stream_close_queued_or_in_flight(*stream) || state == WritableStream::State::Closed)
 | 
						||
        return WebIDL::create_resolved_promise(realm, JS::js_undefined());
 | 
						||
 | 
						||
    // 5. If state is "errored", return a promise rejected with stream.[[storedError]].
 | 
						||
    if (state == WritableStream::State::Errored)
 | 
						||
        return WebIDL::create_rejected_promise(realm, stream->stored_error());
 | 
						||
 | 
						||
    // 6. Assert: state is "writable" or "erroring".
 | 
						||
    VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
 | 
						||
 | 
						||
    // 7. Return ! WritableStreamDefaultWriterClose(writer).
 | 
						||
    return writable_stream_default_writer_close(writer);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-writer-ensure-closed-promise-rejected
 | 
						||
void writable_stream_default_writer_ensure_closed_promise_rejected(WritableStreamDefaultWriter& writer, JS::Value error)
 | 
						||
{
 | 
						||
    auto& realm = writer.realm();
 | 
						||
 | 
						||
    // 1. If writer.[[closedPromise]].[[PromiseState]] is "pending", reject writer.[[closedPromise]] with error.
 | 
						||
    if (as<JS::Promise>(*writer.closed_promise()->promise()).state() == JS::Promise::State::Pending) {
 | 
						||
        WebIDL::reject_promise(realm, *writer.closed_promise(), error);
 | 
						||
    }
 | 
						||
    // 2. Otherwise, set writer.[[closedPromise]] to a promise rejected with error.
 | 
						||
    else {
 | 
						||
        writer.set_closed_promise(WebIDL::create_rejected_promise(realm, error));
 | 
						||
    }
 | 
						||
 | 
						||
    // 3. Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
 | 
						||
    WebIDL::mark_promise_as_handled(*writer.closed_promise());
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-writer-ensure-ready-promise-rejected
 | 
						||
void writable_stream_default_writer_ensure_ready_promise_rejected(WritableStreamDefaultWriter& writer, JS::Value error)
 | 
						||
{
 | 
						||
    auto& realm = writer.realm();
 | 
						||
 | 
						||
    // 1. If writer.[[readyPromise]].[[PromiseState]] is "pending", reject writer.[[readyPromise]] with error.
 | 
						||
    if (as<JS::Promise>(*writer.ready_promise()->promise()).state() == JS::Promise::State::Pending) {
 | 
						||
        WebIDL::reject_promise(realm, *writer.ready_promise(), error);
 | 
						||
    }
 | 
						||
    // 2. Otherwise, set writer.[[readyPromise]] to a promise rejected with error.
 | 
						||
    else {
 | 
						||
        writer.set_ready_promise(WebIDL::create_rejected_promise(realm, error));
 | 
						||
    }
 | 
						||
 | 
						||
    // 3. Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
 | 
						||
    WebIDL::mark_promise_as_handled(*writer.ready_promise());
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-writer-get-desired-size
 | 
						||
Optional<double> writable_stream_default_writer_get_desired_size(WritableStreamDefaultWriter const& writer)
 | 
						||
{
 | 
						||
    // 1. Let stream be writer.[[stream]].
 | 
						||
    auto stream = writer.stream();
 | 
						||
 | 
						||
    // 2. Let state be stream.[[state]].
 | 
						||
    auto state = stream->state();
 | 
						||
 | 
						||
    // 3. If state is "errored" or "erroring", return null.
 | 
						||
    if (state == WritableStream::State::Errored || state == WritableStream::State::Erroring)
 | 
						||
        return {};
 | 
						||
 | 
						||
    // 4. If state is "closed", return 0.
 | 
						||
    if (state == WritableStream::State::Closed)
 | 
						||
        return 0.0;
 | 
						||
 | 
						||
    // 5. Return ! WritableStreamDefaultControllerGetDesiredSize(stream.[[controller]]).
 | 
						||
    return writable_stream_default_controller_get_desired_size(*stream->controller());
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-writer-release
 | 
						||
void writable_stream_default_writer_release(WritableStreamDefaultWriter& writer)
 | 
						||
{
 | 
						||
    auto& realm = writer.realm();
 | 
						||
 | 
						||
    // 1. Let stream be writer.[[stream]].
 | 
						||
    auto stream = writer.stream();
 | 
						||
 | 
						||
    // 2. Assert: stream is not undefined.
 | 
						||
    VERIFY(stream);
 | 
						||
 | 
						||
    // 3. Assert: stream.[[writer]] is writer.
 | 
						||
    VERIFY(stream->writer().ptr() == &writer);
 | 
						||
 | 
						||
    // 4. Let releasedError be a new TypeError.
 | 
						||
    auto released_error = JS::TypeError::create(realm, "Writer's stream lock has been released"sv);
 | 
						||
 | 
						||
    // 5. Perform ! WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError).
 | 
						||
    writable_stream_default_writer_ensure_ready_promise_rejected(writer, released_error);
 | 
						||
 | 
						||
    // 6. Perform ! WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError).
 | 
						||
    writable_stream_default_writer_ensure_closed_promise_rejected(writer, released_error);
 | 
						||
 | 
						||
    // 7. Set stream.[[writer]] to undefined.
 | 
						||
    stream->set_writer({});
 | 
						||
 | 
						||
    // 8. Set writer.[[stream]] to undefined.
 | 
						||
    writer.set_stream({});
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-writer-write
 | 
						||
GC::Ref<WebIDL::Promise> writable_stream_default_writer_write(WritableStreamDefaultWriter& writer, JS::Value chunk)
 | 
						||
{
 | 
						||
    auto& realm = writer.realm();
 | 
						||
 | 
						||
    // 1. Let stream be writer.[[stream]].
 | 
						||
    auto stream = writer.stream();
 | 
						||
 | 
						||
    // 2. Assert: stream is not undefined.
 | 
						||
    VERIFY(stream);
 | 
						||
 | 
						||
    // 3. Let controller be stream.[[controller]].
 | 
						||
    auto controller = stream->controller();
 | 
						||
 | 
						||
    // 4. Let chunkSize be ! WritableStreamDefaultControllerGetChunkSize(controller, chunk).
 | 
						||
    auto chunk_size = writable_stream_default_controller_get_chunk_size(*controller, chunk);
 | 
						||
 | 
						||
    // 5. If stream is not equal to writer.[[stream]], return a promise rejected with a TypeError exception.
 | 
						||
    if (stream.ptr() != writer.stream().ptr()) {
 | 
						||
        auto exception = JS::TypeError::create(realm, "Writer's locked stream changed during write"sv);
 | 
						||
        return WebIDL::create_rejected_promise(realm, exception);
 | 
						||
    }
 | 
						||
 | 
						||
    // 6. Let state be stream.[[state]].
 | 
						||
    auto state = stream->state();
 | 
						||
 | 
						||
    // 7. If state is "errored", return a promise rejected with stream.[[storedError]].
 | 
						||
    if (state == WritableStream::State::Errored)
 | 
						||
        return WebIDL::create_rejected_promise(realm, stream->stored_error());
 | 
						||
 | 
						||
    // 8. If ! WritableStreamCloseQueuedOrInFlight(stream) is true or state is "closed", return a promise rejected with a TypeError exception indicating that the stream is closing or closed.
 | 
						||
    if (writable_stream_close_queued_or_in_flight(*stream) || state == WritableStream::State::Closed) {
 | 
						||
        auto exception = JS::TypeError::create(realm, "Cannot write to a writer whose stream is closing or already closed"sv);
 | 
						||
        return WebIDL::create_rejected_promise(realm, exception);
 | 
						||
    }
 | 
						||
 | 
						||
    // 9. If state is "erroring", return a promise rejected with stream.[[storedError]].
 | 
						||
    if (state == WritableStream::State::Erroring)
 | 
						||
        return WebIDL::create_rejected_promise(realm, stream->stored_error());
 | 
						||
 | 
						||
    // 10. Assert: state is "writable".
 | 
						||
    VERIFY(state == WritableStream::State::Writable);
 | 
						||
 | 
						||
    // 11. Let promise be ! WritableStreamAddWriteRequest(stream).
 | 
						||
    auto promise = writable_stream_add_write_request(*stream);
 | 
						||
 | 
						||
    // 12. Perform ! WritableStreamDefaultControllerWrite(controller, chunk, chunkSize).
 | 
						||
    writable_stream_default_controller_write(*controller, chunk, chunk_size);
 | 
						||
 | 
						||
    // 13. Return promise.
 | 
						||
    return promise;
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller
 | 
						||
WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller(WritableStream& stream, WritableStreamDefaultController& controller, GC::Ref<StartAlgorithm> start_algorithm, GC::Ref<WriteAlgorithm> write_algorithm, GC::Ref<CloseAlgorithm> close_algorithm, GC::Ref<AbortAlgorithm> abort_algorithm, double high_water_mark, GC::Ref<SizeAlgorithm> size_algorithm)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. Assert: stream implements WritableStream.
 | 
						||
 | 
						||
    // 2. Assert: stream.[[controller]] is undefined.
 | 
						||
    VERIFY(!stream.controller());
 | 
						||
 | 
						||
    // 3. Set controller.[[stream]] to stream.
 | 
						||
    controller.set_stream(stream);
 | 
						||
 | 
						||
    // 4. Set stream.[[controller]] to controller.
 | 
						||
    stream.set_controller(controller);
 | 
						||
 | 
						||
    // 5. Perform ! ResetQueue(controller).
 | 
						||
    reset_queue(controller);
 | 
						||
 | 
						||
    // 6. Set controller.[[signal]] to a new AbortSignal.
 | 
						||
    controller.set_signal(realm.create<DOM::AbortSignal>(realm));
 | 
						||
 | 
						||
    // 7. Set controller.[[started]] to false.
 | 
						||
    controller.set_started(false);
 | 
						||
 | 
						||
    // 8. Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm.
 | 
						||
    controller.set_strategy_size_algorithm(size_algorithm);
 | 
						||
 | 
						||
    // 9. Set controller.[[strategyHWM]] to highWaterMark.
 | 
						||
    controller.set_strategy_hwm(high_water_mark);
 | 
						||
 | 
						||
    // 10. Set controller.[[writeAlgorithm]] to writeAlgorithm.
 | 
						||
    controller.set_write_algorithm(write_algorithm);
 | 
						||
 | 
						||
    // 11. Set controller.[[closeAlgorithm]] to closeAlgorithm.
 | 
						||
    controller.set_close_algorithm(close_algorithm);
 | 
						||
 | 
						||
    // 12. Set controller.[[abortAlgorithm]] to abortAlgorithm.
 | 
						||
    controller.set_abort_algorithm(abort_algorithm);
 | 
						||
 | 
						||
    // 13. Let backpressure be ! WritableStreamDefaultControllerGetBackpressure(controller).
 | 
						||
    auto backpressure = writable_stream_default_controller_get_backpressure(controller);
 | 
						||
 | 
						||
    // 14. Perform ! WritableStreamUpdateBackpressure(stream, backpressure).
 | 
						||
    writable_stream_update_backpressure(stream, backpressure);
 | 
						||
 | 
						||
    // 15. Let startResult be the result of performing startAlgorithm. (This may throw an exception.)
 | 
						||
    auto start_result = TRY(start_algorithm->function()());
 | 
						||
 | 
						||
    // 16. Let startPromise be a promise resolved with startResult.
 | 
						||
    auto start_promise = WebIDL::create_resolved_promise(realm, start_result);
 | 
						||
 | 
						||
    WebIDL::react_to_promise(start_promise,
 | 
						||
        // 17. Upon fulfillment of startPromise,
 | 
						||
        GC::create_function(realm.heap(), [&controller, &stream](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
 | 
						||
            // 1. Assert: stream.[[state]] is "writable" or "erroring".
 | 
						||
            VERIFY(first_is_one_of(stream.state(), WritableStream::State::Writable, WritableStream::State::Erroring));
 | 
						||
 | 
						||
            // 2. Set controller.[[started]] to true.
 | 
						||
            controller.set_started(true);
 | 
						||
 | 
						||
            // 3. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
 | 
						||
            writable_stream_default_controller_advance_queue_if_needed(controller);
 | 
						||
 | 
						||
            return JS::js_undefined();
 | 
						||
        }),
 | 
						||
 | 
						||
        // 18. Upon rejection of startPromise with reason r,
 | 
						||
        GC::create_function(realm.heap(), [&stream, &controller](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
 | 
						||
            // 1. Assert: stream.[[state]] is "writable" or "erroring".
 | 
						||
            VERIFY(first_is_one_of(stream.state(), WritableStream::State::Writable, WritableStream::State::Erroring));
 | 
						||
 | 
						||
            // 2. Set controller.[[started]] to true.
 | 
						||
            controller.set_started(true);
 | 
						||
 | 
						||
            // 3. Perform ! WritableStreamDealWithRejection(stream, r).
 | 
						||
            writable_stream_deal_with_rejection(stream, reason);
 | 
						||
 | 
						||
            return JS::js_undefined();
 | 
						||
        }));
 | 
						||
 | 
						||
    return {};
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink
 | 
						||
WebIDL::ExceptionOr<void> set_up_writable_stream_default_controller_from_underlying_sink(WritableStream& stream, JS::Value underlying_sink_value, UnderlyingSink& underlying_sink, double high_water_mark, GC::Ref<SizeAlgorithm> size_algorithm)
 | 
						||
{
 | 
						||
    auto& realm = stream.realm();
 | 
						||
 | 
						||
    // 1. Let controller be a new WritableStreamDefaultController.
 | 
						||
    auto controller = realm.create<WritableStreamDefaultController>(realm);
 | 
						||
 | 
						||
    // 2. Let startAlgorithm be an algorithm that returns undefined.
 | 
						||
    auto start_algorithm = GC::create_function(realm.heap(), []() -> WebIDL::ExceptionOr<JS::Value> { return JS::js_undefined(); });
 | 
						||
 | 
						||
    // 3. Let writeAlgorithm be an algorithm that returns a promise resolved with undefined.
 | 
						||
    auto write_algorithm = GC::create_function(realm.heap(), [&realm](JS::Value) {
 | 
						||
        return WebIDL::create_resolved_promise(realm, JS::js_undefined());
 | 
						||
    });
 | 
						||
 | 
						||
    // 4. Let closeAlgorithm be an algorithm that returns a promise resolved with undefined.
 | 
						||
    auto close_algorithm = GC::create_function(realm.heap(), [&realm]() {
 | 
						||
        return WebIDL::create_resolved_promise(realm, JS::js_undefined());
 | 
						||
    });
 | 
						||
 | 
						||
    // 5. Let abortAlgorithm be an algorithm that returns a promise resolved with undefined.
 | 
						||
    auto abort_algorithm = GC::create_function(realm.heap(), [&realm](JS::Value) {
 | 
						||
        return WebIDL::create_resolved_promise(realm, JS::js_undefined());
 | 
						||
    });
 | 
						||
 | 
						||
    // 6. If underlyingSinkDict["start"] exists, then set startAlgorithm to an algorithm which returns the result of
 | 
						||
    //    invoking underlyingSinkDict["start"] with argument list « controller », exception behavior "rethrow", and
 | 
						||
    //    callback this value underlyingSink.
 | 
						||
    if (underlying_sink.start) {
 | 
						||
        start_algorithm = GC::create_function(realm.heap(), [controller, underlying_sink_value, callback = underlying_sink.start]() -> WebIDL::ExceptionOr<JS::Value> {
 | 
						||
            return TRY(WebIDL::invoke_callback(*callback, underlying_sink_value, WebIDL::ExceptionBehavior::Rethrow, { { controller } }));
 | 
						||
        });
 | 
						||
    }
 | 
						||
 | 
						||
    // 7. If underlyingSinkDict["write"] exists, then set writeAlgorithm to an algorithm which takes an argument chunk
 | 
						||
    //    and returns the result of invoking underlyingSinkDict["write"] with argument list « chunk, controller » and
 | 
						||
    //    callback this value underlyingSink.
 | 
						||
    if (underlying_sink.write) {
 | 
						||
        write_algorithm = GC::create_function(realm.heap(), [controller, underlying_sink_value, callback = underlying_sink.write](JS::Value chunk) {
 | 
						||
            return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, { { chunk, controller } });
 | 
						||
        });
 | 
						||
    }
 | 
						||
 | 
						||
    // 8. If underlyingSinkDict["close"] exists, then set closeAlgorithm to an algorithm which returns the result of
 | 
						||
    //    invoking underlyingSinkDict["close"] with argument list «» and callback this value underlyingSink.
 | 
						||
    if (underlying_sink.close) {
 | 
						||
        close_algorithm = GC::create_function(realm.heap(), [underlying_sink_value, callback = underlying_sink.close]() {
 | 
						||
            return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, {});
 | 
						||
        });
 | 
						||
    }
 | 
						||
 | 
						||
    // 9. If underlyingSinkDict["abort"] exists, then set abortAlgorithm to an algorithm which takes an argument reason
 | 
						||
    //    and returns the result of invoking underlyingSinkDict["abort"] with argument list « reason » and callback this
 | 
						||
    //    value underlyingSink.
 | 
						||
    if (underlying_sink.abort) {
 | 
						||
        abort_algorithm = GC::create_function(realm.heap(), [underlying_sink_value, callback = underlying_sink.abort](JS::Value reason) {
 | 
						||
            return WebIDL::invoke_promise_callback(*callback, underlying_sink_value, { { reason } });
 | 
						||
        });
 | 
						||
    }
 | 
						||
 | 
						||
    // 10. Perform ? SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm).
 | 
						||
    TRY(set_up_writable_stream_default_controller(stream, controller, start_algorithm, write_algorithm, close_algorithm, abort_algorithm, high_water_mark, size_algorithm));
 | 
						||
 | 
						||
    return {};
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-advance-queue-if-needed
 | 
						||
void writable_stream_default_controller_advance_queue_if_needed(WritableStreamDefaultController& controller)
 | 
						||
{
 | 
						||
    // 1. Let stream be controller.[[stream]].
 | 
						||
    auto stream = controller.stream();
 | 
						||
 | 
						||
    // 2. If controller.[[started]] is false, return.
 | 
						||
    if (!controller.started())
 | 
						||
        return;
 | 
						||
 | 
						||
    // 3. If stream.[[inFlightWriteRequest]] is not undefined, return.
 | 
						||
    if (stream->in_flight_write_request())
 | 
						||
        return;
 | 
						||
 | 
						||
    // 4. Let state be stream.[[state]].
 | 
						||
    auto state = stream->state();
 | 
						||
 | 
						||
    // 5. Assert: state is not "closed" or "errored".
 | 
						||
    VERIFY(state != WritableStream::State::Closed && state != WritableStream::State::Errored);
 | 
						||
 | 
						||
    // 6. If state is "erroring",
 | 
						||
    if (state == WritableStream::State::Erroring) {
 | 
						||
        // 1. Perform ! WritableStreamFinishErroring(stream).
 | 
						||
        writable_stream_finish_erroring(stream);
 | 
						||
 | 
						||
        // 2. Return.
 | 
						||
        return;
 | 
						||
    }
 | 
						||
 | 
						||
    // 7. If controller.[[queue]] is empty, return.
 | 
						||
    if (controller.queue().is_empty())
 | 
						||
        return;
 | 
						||
 | 
						||
    // 8. Let value be ! PeekQueueValue(controller).
 | 
						||
    auto value = peek_queue_value(controller);
 | 
						||
 | 
						||
    // 9. If value is the close sentinel, perform ! WritableStreamDefaultControllerProcessClose(controller).
 | 
						||
    if (is_close_sentinel(value)) {
 | 
						||
        writable_stream_default_controller_process_close(controller);
 | 
						||
    }
 | 
						||
    // 10. Otherwise, perform ! WritableStreamDefaultControllerProcessWrite(controller, value).
 | 
						||
    else {
 | 
						||
        writable_stream_default_controller_process_write(controller, value);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-clear-algorithms
 | 
						||
void writable_stream_default_controller_clear_algorithms(WritableStreamDefaultController& controller)
 | 
						||
{
 | 
						||
    // 1. Set controller.[[writeAlgorithm]] to undefined.
 | 
						||
    controller.set_write_algorithm({});
 | 
						||
 | 
						||
    // 2. Set controller.[[closeAlgorithm]] to undefined.
 | 
						||
    controller.set_close_algorithm({});
 | 
						||
 | 
						||
    // 3. Set controller.[[abortAlgorithm]] to undefined.
 | 
						||
    controller.set_abort_algorithm({});
 | 
						||
 | 
						||
    // 4. Set controller.[[strategySizeAlgorithm]] to undefined.
 | 
						||
    controller.set_strategy_size_algorithm({});
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-close
 | 
						||
void writable_stream_default_controller_close(WritableStreamDefaultController& controller)
 | 
						||
{
 | 
						||
    // 1. Perform ! EnqueueValueWithSize(controller, close sentinel, 0).
 | 
						||
    MUST(enqueue_value_with_size(controller, create_close_sentinel(), JS::Value(0.0)));
 | 
						||
 | 
						||
    // 2. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
 | 
						||
    writable_stream_default_controller_advance_queue_if_needed(controller);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-error
 | 
						||
void writable_stream_default_controller_error(WritableStreamDefaultController& controller, JS::Value error)
 | 
						||
{
 | 
						||
    // 1. Let stream be controller.[[stream]].
 | 
						||
    auto stream = controller.stream();
 | 
						||
 | 
						||
    // 2. Assert: stream.[[state]] is "writable".
 | 
						||
    VERIFY(stream->state() == WritableStream::State::Writable);
 | 
						||
 | 
						||
    // 3. Perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
 | 
						||
    writable_stream_default_controller_clear_algorithms(controller);
 | 
						||
 | 
						||
    // 4. Perform ! WritableStreamStartErroring(stream, error).
 | 
						||
    writable_stream_start_erroring(stream, error);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-error-if-needed
 | 
						||
void writable_stream_default_controller_error_if_needed(WritableStreamDefaultController& controller, JS::Value error)
 | 
						||
{
 | 
						||
    // 1. If controller.[[stream]].[[state]] is "writable", perform ! WritableStreamDefaultControllerError(controller, error).
 | 
						||
    if (controller.stream()->state() == WritableStream::State::Writable)
 | 
						||
        writable_stream_default_controller_error(controller, error);
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-get-backpressure
 | 
						||
bool writable_stream_default_controller_get_backpressure(WritableStreamDefaultController const& controller)
 | 
						||
{
 | 
						||
    // 1. Let desiredSize be ! WritableStreamDefaultControllerGetDesiredSize(controller).
 | 
						||
    auto desired_size = writable_stream_default_controller_get_desired_size(controller);
 | 
						||
 | 
						||
    // 2. Return true if desiredSize ≤ 0, or false otherwise.
 | 
						||
    return desired_size <= 0.0;
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-get-chunk-size
 | 
						||
JS::Value writable_stream_default_controller_get_chunk_size(WritableStreamDefaultController& controller, JS::Value chunk)
 | 
						||
{
 | 
						||
    // 1. If controller.[[strategySizeAlgorithm]] is undefined, then:
 | 
						||
    if (!controller.strategy_size_algorithm()) {
 | 
						||
        // 1. Assert: controller.[[stream]].[[state]] is not "writable".
 | 
						||
        VERIFY(controller.stream()->state() != WritableStream::State::Writable);
 | 
						||
 | 
						||
        // 2. Return 1.
 | 
						||
        return JS::Value { 1.0 };
 | 
						||
    }
 | 
						||
 | 
						||
    // 2. Let returnValue be the result of performing controller.[[strategySizeAlgorithm]], passing in chunk, and interpreting the result as a completion record.
 | 
						||
    auto return_value = controller.strategy_size_algorithm()->function()(chunk);
 | 
						||
 | 
						||
    // 3. If returnValue is an abrupt completion,
 | 
						||
    if (return_value.is_abrupt()) {
 | 
						||
        // 1. Perform ! WritableStreamDefaultControllerErrorIfNeeded(controller, returnValue.[[Value]]).
 | 
						||
        writable_stream_default_controller_error_if_needed(controller, return_value.release_value());
 | 
						||
 | 
						||
        // 2. Return 1.
 | 
						||
        return JS::Value { 1.0 };
 | 
						||
    }
 | 
						||
 | 
						||
    // 4. Return returnValue.[[Value]].
 | 
						||
    return return_value.release_value();
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-get-desired-size
 | 
						||
double writable_stream_default_controller_get_desired_size(WritableStreamDefaultController const& controller)
 | 
						||
{
 | 
						||
    // 1. Return controller.[[strategyHWM]] − controller.[[queueTotalSize]].
 | 
						||
    return controller.strategy_hwm() - controller.queue_total_size();
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-process-close
 | 
						||
void writable_stream_default_controller_process_close(WritableStreamDefaultController& controller)
 | 
						||
{
 | 
						||
    // 1. Let stream be controller.[[stream]].
 | 
						||
    auto stream = controller.stream();
 | 
						||
 | 
						||
    // 2. Perform ! WritableStreamMarkCloseRequestInFlight(stream).
 | 
						||
    writable_stream_mark_close_request_in_flight(stream);
 | 
						||
 | 
						||
    // 3. Perform ! DequeueValue(controller).
 | 
						||
    dequeue_value(controller);
 | 
						||
 | 
						||
    // 4. Assert: controller.[[queue]] is empty.
 | 
						||
    VERIFY(controller.queue().is_empty());
 | 
						||
 | 
						||
    // 5. Let sinkClosePromise be the result of performing controller.[[closeAlgorithm]].
 | 
						||
    auto sink_close_promise = controller.close_algorithm()->function()();
 | 
						||
 | 
						||
    // 6. Perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
 | 
						||
    writable_stream_default_controller_clear_algorithms(controller);
 | 
						||
 | 
						||
    WebIDL::react_to_promise(sink_close_promise,
 | 
						||
        // 7. Upon fulfillment of sinkClosePromise,
 | 
						||
        GC::create_function(controller.heap(), [stream](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
 | 
						||
            // 1. Perform ! WritableStreamFinishInFlightClose(stream).
 | 
						||
            writable_stream_finish_in_flight_close(stream);
 | 
						||
 | 
						||
            return JS::js_undefined();
 | 
						||
        }),
 | 
						||
 | 
						||
        // 8. Upon rejection of sinkClosePromise with reason reason,
 | 
						||
        GC::create_function(controller.heap(), [stream](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
 | 
						||
            // 1. Perform ! WritableStreamFinishInFlightCloseWithError(stream, reason).
 | 
						||
            writable_stream_finish_in_flight_close_with_error(stream, reason);
 | 
						||
 | 
						||
            return JS::js_undefined();
 | 
						||
        }));
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-process-write
 | 
						||
void writable_stream_default_controller_process_write(WritableStreamDefaultController& controller, JS::Value chunk)
 | 
						||
{
 | 
						||
    // 1. Let stream be controller.[[stream]].
 | 
						||
    auto stream = controller.stream();
 | 
						||
 | 
						||
    // 2. Perform ! WritableStreamMarkFirstWriteRequestInFlight(stream).
 | 
						||
    writable_stream_mark_first_write_request_in_flight(stream);
 | 
						||
 | 
						||
    // 3. Let sinkWritePromise be the result of performing controller.[[writeAlgorithm]], passing in chunk.
 | 
						||
    auto sink_write_promise = controller.write_algorithm()->function()(chunk);
 | 
						||
 | 
						||
    WebIDL::react_to_promise(sink_write_promise,
 | 
						||
        // 4. Upon fulfillment of sinkWritePromise,
 | 
						||
        GC::create_function(controller.heap(), [&controller, stream](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
 | 
						||
            // 1. Perform ! WritableStreamFinishInFlightWrite(stream).
 | 
						||
            writable_stream_finish_in_flight_write(stream);
 | 
						||
 | 
						||
            // 2. Let state be stream.[[state]].
 | 
						||
            auto state = stream->state();
 | 
						||
 | 
						||
            // 3. Assert: state is "writable" or "erroring".
 | 
						||
            VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
 | 
						||
 | 
						||
            // 4. Perform ! DequeueValue(controller).
 | 
						||
            dequeue_value(controller);
 | 
						||
 | 
						||
            // 5. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and state is "writable",
 | 
						||
            if (!writable_stream_close_queued_or_in_flight(stream) && state == WritableStream::State::Writable) {
 | 
						||
                // 1. Let backpressure be ! WritableStreamDefaultControllerGetBackpressure(controller).
 | 
						||
                auto backpressure = writable_stream_default_controller_get_backpressure(controller);
 | 
						||
 | 
						||
                // 2. Perform ! WritableStreamUpdateBackpressure(stream, backpressure).
 | 
						||
                writable_stream_update_backpressure(stream, backpressure);
 | 
						||
            }
 | 
						||
 | 
						||
            // 6 .Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
 | 
						||
            writable_stream_default_controller_advance_queue_if_needed(controller);
 | 
						||
 | 
						||
            return JS::js_undefined();
 | 
						||
        }),
 | 
						||
 | 
						||
        // 5. Upon rejection of sinkWritePromise with reason,
 | 
						||
        GC::create_function(controller.heap(), [&controller, stream](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
 | 
						||
            // 1. If stream.[[state]] is "writable", perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
 | 
						||
            if (stream->state() == WritableStream::State::Writable)
 | 
						||
                writable_stream_default_controller_clear_algorithms(controller);
 | 
						||
 | 
						||
            // 2. Perform ! WritableStreamFinishInFlightWriteWithError(stream, reason).
 | 
						||
            writable_stream_finish_in_flight_write_with_error(stream, reason);
 | 
						||
 | 
						||
            return JS::js_undefined();
 | 
						||
        }));
 | 
						||
}
 | 
						||
 | 
						||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-write
 | 
						||
void writable_stream_default_controller_write(WritableStreamDefaultController& controller, JS::Value chunk, JS::Value chunk_size)
 | 
						||
{
 | 
						||
    auto& vm = controller.vm();
 | 
						||
 | 
						||
    // 1. Let enqueueResult be EnqueueValueWithSize(controller, chunk, chunkSize).
 | 
						||
    auto enqueue_result = enqueue_value_with_size(controller, chunk, chunk_size);
 | 
						||
 | 
						||
    // 2. If enqueueResult is an abrupt completion,
 | 
						||
    if (enqueue_result.is_exception()) {
 | 
						||
        auto throw_completion = Bindings::throw_dom_exception_if_needed(vm, [&] { return enqueue_result; }).throw_completion();
 | 
						||
 | 
						||
        // 1. Perform ! WritableStreamDefaultControllerErrorIfNeeded(controller, enqueueResult.[[Value]]).
 | 
						||
        writable_stream_default_controller_error_if_needed(controller, throw_completion.release_value());
 | 
						||
 | 
						||
        // 2. Return.
 | 
						||
        return;
 | 
						||
    }
 | 
						||
 | 
						||
    // 3. Let stream be controller.[[stream]].
 | 
						||
    auto stream = controller.stream();
 | 
						||
 | 
						||
    // 4. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and stream.[[state]] is "writable",
 | 
						||
    if (!writable_stream_close_queued_or_in_flight(stream) && stream->state() == WritableStream::State::Writable) {
 | 
						||
        // 1. Let backpressure be ! WritableStreamDefaultControllerGetBackpressure(controller).
 | 
						||
        auto backpressure = writable_stream_default_controller_get_backpressure(controller);
 | 
						||
 | 
						||
        // 2. Perform ! WritableStreamUpdateBackpressure(stream, backpressure).
 | 
						||
        writable_stream_update_backpressure(stream, backpressure);
 | 
						||
    }
 | 
						||
 | 
						||
    // 5. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
 | 
						||
    writable_stream_default_controller_advance_queue_if_needed(controller);
 | 
						||
}
 | 
						||
 | 
						||
}
 |