blob: 3d6680a03d17d55424273087c7e1a9a47e10293a [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/streams/writable_stream_default_controller.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_writable_stream_default_controller.h"
#include "third_party/blink/renderer/core/streams/miscellaneous_operations.h"
#include "third_party/blink/renderer/core/streams/promise_handler.h"
#include "third_party/blink/renderer/core/streams/queue_with_sizes.h"
#include "third_party/blink/renderer/core/streams/stream_algorithms.h"
#include "third_party/blink/renderer/core/streams/writable_stream.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/bindings/to_v8.h"
#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/heap/visitor.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
namespace blink {
WritableStreamDefaultController* WritableStreamDefaultController::From(
ScriptValue controller) {
DCHECK(controller.IsObject());
return V8WritableStreamDefaultController::ToImpl(
controller.V8Value().As<v8::Object>());
}
// Only used internally. Not reachable from JavaScript.
WritableStreamDefaultController::WritableStreamDefaultController()
: queue_(MakeGarbageCollected<QueueWithSizes>()) {}
void WritableStreamDefaultController::error(ScriptState* script_state) {
error(script_state, ScriptValue(script_state->GetIsolate(),
v8::Undefined(script_state->GetIsolate())));
}
void WritableStreamDefaultController::error(ScriptState* script_state,
ScriptValue e) {
// https://streams.spec.whatwg.org/#ws-default-controller-error
// 2. Let state be this.[[controlledWritableStream]].[[state]].
const auto state = controlled_writable_stream_->GetState();
// 3. If state is not "writable", return.
if (state != WritableStream::kWritable) {
return;
}
// 4. Perform ! WritableStreamDefaultControllerError(this, e).
Error(script_state, this, e.V8Value());
}
// Writable Stream Default Controller Internal Methods
v8::Local<v8::Promise> WritableStreamDefaultController::AbortSteps(
ScriptState* script_state,
v8::Local<v8::Value> reason) {
// https://streams.spec.whatwg.org/#ws-default-controller-private-abort
// 1. Let result be the result of performing this.[[abortAlgorithm]], passing
// reason.
const auto result = abort_algorithm_->Run(script_state, 1, &reason);
// 2. Perform ! WritableStreamDefaultControllerClearAlgorithms(this).
ClearAlgorithms(this);
// 3. Return result.
return result;
}
void WritableStreamDefaultController::ErrorSteps() {
// https://streams.spec.whatwg.org/#ws-default-controller-private-error
// 1. Perform ! ResetQueue(this).
queue_->ResetQueue();
}
// Writable Stream Default Controller Abstract Operations
// TODO(ricea): Should this be a constructor?
void WritableStreamDefaultController::SetUp(
ScriptState* script_state,
WritableStream* stream,
WritableStreamDefaultController* controller,
StreamStartAlgorithm* start_algorithm,
StreamAlgorithm* write_algorithm,
StreamAlgorithm* close_algorithm,
StreamAlgorithm* abort_algorithm,
double high_water_mark,
StrategySizeAlgorithm* size_algorithm,
ExceptionState& exception_state) {
// https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller
// 2. Assert: stream.[[writableStreamController]] is undefined.
DCHECK(!stream->Controller());
// 3. Set controller.[[controlledWritableStream]] to stream.
controller->controlled_writable_stream_ = stream;
// 4. Set stream.[[writableStreamController]] to controller.
stream->SetController(controller);
// Step not needed because queue is initialised during construction.
// 5. Perform ! ResetQueue(controller).
// 6. Set controller.[[started]] to false.
controller->started_ = false;
// 7. Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm.
controller->strategy_size_algorithm_ = size_algorithm;
// 8. Set controller.[[strategyHWM]] to highWaterMark.
controller->strategy_high_water_mark_ = high_water_mark;
// 9. Set controller.[[writeAlgorithm]] to writeAlgorithm.
controller->write_algorithm_ = write_algorithm;
// 10. Set controller.[[closeAlgorithm]] to closeAlgorithm.
controller->close_algorithm_ = close_algorithm;
// 11. Set controller.[[abortAlgorithm]] to abortAlgorithm.
controller->abort_algorithm_ = abort_algorithm;
// 12. Let backpressure be !
// WritableStreamDefaultControllerGetBackpressure(controller).
const bool backpressure = GetBackpressure(controller);
// 13. Perform ! WritableStreamUpdateBackpressure(stream, backpressure).
WritableStream::UpdateBackpressure(script_state, stream, backpressure);
// 14. Let startResult be the result of performing startAlgorithm. (This may
// throw an exception.)
// In this implementation, start_algorithm returns a Promise when it doesn't
// throw.
// 15. Let startPromise be a promise resolved with startResult.
v8::Local<v8::Promise> start_promise;
if (!start_algorithm->Run(script_state, exception_state)
.ToLocal(&start_promise)) {
if (!exception_state.HadException()) {
// Is this block really needed? Can we make this a DCHECK?
exception_state.ThrowException(
static_cast<int>(DOMExceptionCode::kInvalidStateError),
"start algorithm failed with no exception thrown");
}
return;
}
DCHECK(!exception_state.HadException());
class ResolvePromiseFunction final : public PromiseHandler {
public:
ResolvePromiseFunction(ScriptState* script_state, WritableStream* stream)
: PromiseHandler(script_state), stream_(stream) {}
void CallWithLocal(v8::Local<v8::Value>) override {
// 16. Upon fulfillment of startPromise
// a. Assert: stream.[[state]] is "writable" or "erroring".
const auto state = stream_->GetState();
CHECK(state == WritableStream::kWritable ||
state == WritableStream::kErroring);
// b. Set controller.[[started]] to true.
WritableStreamDefaultController* controller = stream_->Controller();
controller->started_ = true;
// c. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(
// controller).
WritableStreamDefaultController::AdvanceQueueIfNeeded(GetScriptState(),
controller);
}
void Trace(Visitor* visitor) override {
visitor->Trace(stream_);
PromiseHandler::Trace(visitor);
}
private:
Member<WritableStream> stream_;
};
class RejectPromiseFunction final : public PromiseHandler {
public:
RejectPromiseFunction(ScriptState* script_state, WritableStream* stream)
: PromiseHandler(script_state), stream_(stream) {}
void CallWithLocal(v8::Local<v8::Value> r) override {
// 17. Upon rejection of startPromise with reason r,
// a. Assert: stream.[[state]] is "writable" or "erroring".
const auto state = stream_->GetState();
CHECK(state == WritableStream::kWritable ||
state == WritableStream::kErroring);
// b. Set controller.[[started]] to true.
WritableStreamDefaultController* controller = stream_->Controller();
controller->started_ = true;
// c. Perform ! WritableStreamDealWithRejection(stream, r).
WritableStream::DealWithRejection(GetScriptState(), stream_, r);
}
void Trace(Visitor* visitor) override {
visitor->Trace(stream_);
PromiseHandler::Trace(visitor);
}
private:
Member<WritableStream> stream_;
};
StreamThenPromise(
script_state->GetContext(), start_promise,
MakeGarbageCollected<ResolvePromiseFunction>(script_state, stream),
MakeGarbageCollected<RejectPromiseFunction>(script_state, stream));
}
// TODO(ricea): Should this be a constructor?
void WritableStreamDefaultController::SetUpFromUnderlyingSink(
ScriptState* script_state,
WritableStream* stream,
v8::Local<v8::Object> underlying_sink,
double high_water_mark,
StrategySizeAlgorithm* size_algorithm,
ExceptionState& exception_state) {
// https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink
// 1. Assert: underlyingSink is not undefined.
DCHECK(!underlying_sink.IsEmpty());
// 2. Let controller be ObjectCreate(the original value of
// WritableStreamDefaultController's prototype property).
auto* controller = MakeGarbageCollected<WritableStreamDefaultController>();
// This method is only called when a WritableStream is being constructed by
// JavaScript. So the execution context should be valid and this call should
// not crash.
auto controller_value = ToV8(controller, script_state);
// 3. Let startAlgorithm be the following steps:
// a. Return ? InvokeOrNoop(underlyingSink, "start", « controller »).
auto* start_algorithm = CreateStartAlgorithm(
script_state, underlying_sink, "underlyingSink.start", controller_value);
// 4. Let writeAlgorithm be ? CreateAlgorithmFromUnderlyingMethod(
// underlyingSink, "write", 1, « controller »).
auto* write_algorithm = CreateAlgorithmFromUnderlyingMethod(
script_state, underlying_sink, "write", "underlyingSink.write",
controller_value, exception_state);
if (exception_state.HadException()) {
return;
}
DCHECK(write_algorithm);
// 5. Let closeAlgorithm be ? CreateAlgorithmFromUnderlyingMethod(
// underlyingSink, "close", 0, « »).
auto* close_algorithm = CreateAlgorithmFromUnderlyingMethod(
script_state, underlying_sink, "close", "underlyingSink.close",
v8::MaybeLocal<v8::Value>(), exception_state);
if (exception_state.HadException()) {
return;
}
DCHECK(close_algorithm);
// 6. Let abortAlgorithm be ? CreateAlgorithmFromUnderlyingMethod(
// underlyingSink, "abort", 1, « »).
auto* abort_algorithm = CreateAlgorithmFromUnderlyingMethod(
script_state, underlying_sink, "abort", "underlyingSink.abort",
v8::MaybeLocal<v8::Value>(), exception_state);
if (exception_state.HadException()) {
return;
}
DCHECK(abort_algorithm);
// 7. Perform ? SetUpWritableStreamDefaultController(stream, controller,
// startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm,
// highWaterMark, sizeAlgorithm).
SetUp(script_state, stream, controller, start_algorithm, write_algorithm,
close_algorithm, abort_algorithm, high_water_mark, size_algorithm,
exception_state);
}
void WritableStreamDefaultController::Close(
ScriptState* script_state,
WritableStreamDefaultController* controller) {
// https://streams.spec.whatwg.org/#writable-stream-default-controller-close
// 1. Perform ! EnqueueValueWithSize(controller, "close", 0).
// The |close_queued_| flag represents the presence of the `"close"` marker
// in the queue.
controller->close_queued_ = true;
// 2. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(
// controller).
AdvanceQueueIfNeeded(script_state, controller);
}
double WritableStreamDefaultController::GetChunkSize(
ScriptState* script_state,
WritableStreamDefaultController* controller,
v8::Local<v8::Value> chunk) {
if (!controller->strategy_size_algorithm_) {
DCHECK_NE(controller->controlled_writable_stream_->GetState(),
WritableStream::kWritable);
// No need to error since the stream is already stopped or stopping.
return 1;
}
ExceptionState exception_state(script_state->GetIsolate(),
ExceptionState::kUnknownContext, "", "");
// https://streams.spec.whatwg.org/#writable-stream-default-controller-get-chunk-size
// 1. Let returnValue be the result of performing
// controller.[[strategySizeAlgorithm]], passing in chunk, and
// interpreting the result as an ECMAScript completion value.
auto return_value = controller->strategy_size_algorithm_->Run(
script_state, chunk, exception_state);
// 2. If returnValue is an abrupt completion,
if (!return_value.has_value()) {
// a. Perform ! WritableStreamDefaultControllerErrorIfNeeded(
// controller, returnValue.[[Value]]).
ErrorIfNeeded(script_state, controller, exception_state.GetException());
exception_state.ClearException();
// b. Return 1.
return 1;
}
// 3. Return returnValue.[[Value]].
return return_value.value();
}
double WritableStreamDefaultController::GetDesiredSize(
const WritableStreamDefaultController* controller) {
// https://streams.spec.whatwg.org/#writable-stream-default-controller-get-desired-size
// 1. Return controller.[[strategyHWM]] − controller.[[queueTotalSize]].
return controller->strategy_high_water_mark_ -
controller->queue_->TotalSize();
}
void WritableStreamDefaultController::Write(
ScriptState* script_state,
WritableStreamDefaultController* controller,
v8::Local<v8::Value> chunk,
double chunk_size) {
// https://streams.spec.whatwg.org/#writable-stream-default-controller-write
// The chunk is represented literally in the queue, rather than being embedded
// in an object, so the following step is not performed:
// 1. Let writeRecord be Record {[[chunk]]: chunk}.
{
ExceptionState exception_state(script_state->GetIsolate(),
ExceptionState::kUnknownContext, "", "");
// 2. Let enqueueResult be EnqueueValueWithSize(controller, writeRecord,
// chunkSize).
controller->queue_->EnqueueValueWithSize(script_state->GetIsolate(), chunk,
chunk_size, exception_state);
// 3. If enqueueResult is an abrupt completion,
if (exception_state.HadException()) {
// a. Perform ! WritableStreamDefaultControllerErrorIfNeeded(
// controller, enqueueResult.[[Value]]).
ErrorIfNeeded(script_state, controller, exception_state.GetException());
exception_state.ClearException();
// b. Return.
return;
}
}
// 4. Let stream be controller.[[controlledWritableStream]].
WritableStream* stream = controller->controlled_writable_stream_;
// 5. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and
// stream.[[state]] is "writable",
if (!WritableStream::CloseQueuedOrInFlight(stream) &&
stream->GetState() == WritableStream::kWritable) {
// a. Let backpressure be !
// WritableStreamDefaultControllerGetBackpressure(controller).
const bool backpressure = GetBackpressure(controller);
// b. Perform ! WritableStreamUpdateBackpressure(stream, backpressure).
WritableStream::UpdateBackpressure(script_state, stream, backpressure);
}
// 6. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(
// controller).
AdvanceQueueIfNeeded(script_state, controller);
}
void WritableStreamDefaultController::ErrorIfNeeded(
ScriptState* script_state,
WritableStreamDefaultController* controller,
v8::Local<v8::Value> error) {
// https://streams.spec.whatwg.org/#writable-stream-default-controller-error-if-needed
// 1. If controller.[[controlledWritableStream]].[[state]] is "writable",
// perform ! WritableStreamDefaultControllerError(controller, error).
const auto state = controller->controlled_writable_stream_->GetState();
if (state == WritableStream::kWritable) {
Error(script_state, controller, error);
}
}
void WritableStreamDefaultController::Trace(Visitor* visitor) {
visitor->Trace(abort_algorithm_);
visitor->Trace(close_algorithm_);
visitor->Trace(controlled_writable_stream_);
visitor->Trace(queue_);
visitor->Trace(strategy_size_algorithm_);
visitor->Trace(write_algorithm_);
ScriptWrappable::Trace(visitor);
}
void WritableStreamDefaultController::ClearAlgorithms(
WritableStreamDefaultController* controller) {
// https://streams.spec.whatwg.org/#writable-stream-default-controller-clear-algorithms
// 1. Set controller.[[writeAlgorithm]] to undefined.
controller->write_algorithm_ = nullptr;
// 2. Set controller.[[closeAlgorithm]] to undefined.
controller->close_algorithm_ = nullptr;
// 3. Set controller.[[abortAlgorithm]] to undefined.
controller->abort_algorithm_ = nullptr;
// 4. Set controller.[[strategySizeAlgorithm]] to undefined.
controller->strategy_size_algorithm_ = nullptr;
}
void WritableStreamDefaultController::AdvanceQueueIfNeeded(
ScriptState* script_state,
WritableStreamDefaultController* controller) {
// https://streams.spec.whatwg.org/#writable-stream-default-controller-advance-queue-if-needed
// 1. Let stream be controller.[[controlledWritableStream]].
WritableStream* stream = controller->controlled_writable_stream_;
// 2. If controller.[[started]] is false, return
if (!controller->started_) {
return;
}
// 3. If stream.[[inFlightWriteRequest]] is not undefined, return.
if (stream->InFlightWriteRequest()) {
return;
}
// 4. Let state be stream.[[state]].
const auto state = stream->GetState();
// 5. If state is "closed" or "errored", return.
if (state == WritableStream::kClosed || state == WritableStream::kErrored) {
return;
}
// 6. If state is "erroring",
if (state == WritableStream::kErroring) {
// a. Perform ! WritableStreamFinishErroring(stream).
WritableStream::FinishErroring(script_state, stream);
// b. Return.
return;
}
// 7. If controller.[[queue]] is empty, return.
if (controller->queue_->IsEmpty()) {
// Empty queue + |close_queued_| true implies `"close"` marker in queue.
// 9. If writeRecord is "close", perform !
// WritableStreamDefaultControllerProcessClose(controller).
if (controller->close_queued_) {
ProcessClose(script_state, controller);
}
return;
}
// 8. Let writeRecord be ! PeekQueueValue(controller).
const auto chunk =
controller->queue_->PeekQueueValue(script_state->GetIsolate());
// 10. Otherwise, perform ! WritableStreamDefaultControllerProcessWrite(
// controller, writeRecord.[[chunk]]).
// ("Otherwise" here means if the chunk is not a `"close"` marker).
WritableStreamDefaultController::ProcessWrite(script_state, controller,
chunk);
}
void WritableStreamDefaultController::ProcessClose(
ScriptState* script_state,
WritableStreamDefaultController* controller) {
// https://streams.spec.whatwg.org/#writable-stream-default-controller-process-close
// 1. Let stream be controller.[[controlledWritableStream]].
WritableStream* stream = controller->controlled_writable_stream_;
// 2. Perform ! WritableStreamMarkCloseRequestInFlight(stream).
WritableStream::MarkCloseRequestInFlight(stream);
// 3. Perform ! DequeueValue(controller).
// Here we "dequeue" the `"close"` marker, which is implied by the
// |close_queued_| flag, by unsetting the flag.
// 4. Assert: controller.[[queue]] is empty.
DCHECK(controller->queue_->IsEmpty());
DCHECK(controller->close_queued_);
controller->close_queued_ = false;
// 5. Let sinkClosePromise be the result of performing
// controller.[[closeAlgorithm]].
const auto sinkClosePromise =
controller->close_algorithm_->Run(script_state, 0, nullptr);
// 6. Perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
ClearAlgorithms(controller);
class ResolveFunction final : public PromiseHandler {
public:
ResolveFunction(ScriptState* script_state, WritableStream* stream)
: PromiseHandler(script_state), stream_(stream) {}
void CallWithLocal(v8::Local<v8::Value>) override {
// 7. Upon fulfillment of sinkClosePromise,
// a. Perform ! WritableStreamFinishInFlightClose(stream).
WritableStream::FinishInFlightClose(GetScriptState(), stream_);
}
void Trace(Visitor* visitor) override {
visitor->Trace(stream_);
PromiseHandler::Trace(visitor);
}
private:
Member<WritableStream> stream_;
};
class RejectFunction final : public PromiseHandler {
public:
RejectFunction(ScriptState* script_state, WritableStream* stream)
: PromiseHandler(script_state), stream_(stream) {}
void CallWithLocal(v8::Local<v8::Value> reason) override {
// 8. Upon rejection of sinkClosePromise with reason reason,
// a. Perform ! WritableStreamFinishInFlightCloseWithError(stream,
// reason).
WritableStream::FinishInFlightCloseWithError(GetScriptState(), stream_,
reason);
}
void Trace(Visitor* visitor) override {
visitor->Trace(stream_);
PromiseHandler::Trace(visitor);
}
private:
Member<WritableStream> stream_;
};
StreamThenPromise(script_state->GetContext(), sinkClosePromise,
MakeGarbageCollected<ResolveFunction>(script_state, stream),
MakeGarbageCollected<RejectFunction>(script_state, stream));
}
void WritableStreamDefaultController::ProcessWrite(
ScriptState* script_state,
WritableStreamDefaultController* controller,
v8::Local<v8::Value> chunk) {
// https://streams.spec.whatwg.org/#writable-stream-default-controller-process-write
// 1. Let stream be controller.[[controlledWritableStream]].
WritableStream* stream = controller->controlled_writable_stream_;
// 2. Perform ! WritableStreamMarkFirstWriteRequestInFlight(stream).
WritableStream::MarkFirstWriteRequestInFlight(stream);
// 3. Let sinkWritePromise be the result of performing
// controller.[[writeAlgorithm]], passing in chunk.
const auto sinkWritePromise =
controller->write_algorithm_->Run(script_state, 1, &chunk);
class ResolveFunction final : public PromiseHandler {
public:
ResolveFunction(ScriptState* script_state,
WritableStream* stream,
WritableStreamDefaultController* controller)
: PromiseHandler(script_state),
stream_(stream),
controller_(controller) {}
void CallWithLocal(v8::Local<v8::Value>) override {
auto* script_state = GetScriptState();
// 4. Upon fulfillment of sinkWritePromise,
// a. Perform ! WritableStreamFinishInFlightWrite(stream).
WritableStream::FinishInFlightWrite(script_state, stream_);
// b. Let state be stream.[[state]].
const auto state = stream_->GetState();
// c. Assert: state is "writable" or "erroring".
CHECK(state == WritableStream::kWritable ||
state == WritableStream::kErroring);
// d. Perform ! DequeueValue(controller).
controller_->queue_->DequeueValue(script_state->GetIsolate());
// e. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and
// state is "writable",
if (!WritableStream::CloseQueuedOrInFlight(stream_) &&
state == WritableStream::kWritable) {
// i. Let backpressure be !
// WritableStreamDefaultControllerGetBackpressure(
// controller).
const bool backpressure =
WritableStreamDefaultController::GetBackpressure(controller_);
// ii. Perform ! WritableStreamUpdateBackpressure(stream,
// backpressure).
WritableStream::UpdateBackpressure(script_state, stream_, backpressure);
}
// f. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(
// controller).
WritableStreamDefaultController::AdvanceQueueIfNeeded(script_state,
controller_);
}
void Trace(Visitor* visitor) override {
visitor->Trace(stream_);
visitor->Trace(controller_);
PromiseHandler::Trace(visitor);
}
private:
Member<WritableStream> stream_;
Member<WritableStreamDefaultController> controller_;
};
class RejectFunction final : public PromiseHandler {
public:
RejectFunction(ScriptState* script_state,
WritableStream* stream,
WritableStreamDefaultController* controller)
: PromiseHandler(script_state),
stream_(stream),
controller_(controller) {}
void CallWithLocal(v8::Local<v8::Value> reason) override {
const auto state = stream_->GetState();
// 5. Upon rejection of sinkWritePromise with reason,
// a. If stream.[[state]] is "writable", perform !
// WritableStreamDefaultControllerClearAlgorithms(controller).
if (state == WritableStream::kWritable) {
WritableStreamDefaultController::ClearAlgorithms(controller_);
}
// b. Perform ! WritableStreamFinishInFlightWriteWithError(stream,
// reason).
WritableStream::FinishInFlightWriteWithError(GetScriptState(), stream_,
reason);
}
void Trace(Visitor* visitor) override {
visitor->Trace(stream_);
visitor->Trace(controller_);
PromiseHandler::Trace(visitor);
}
private:
Member<WritableStream> stream_;
Member<WritableStreamDefaultController> controller_;
};
StreamThenPromise(
script_state->GetContext(), sinkWritePromise,
MakeGarbageCollected<ResolveFunction>(script_state, stream, controller),
MakeGarbageCollected<RejectFunction>(script_state, stream, controller));
}
bool WritableStreamDefaultController::GetBackpressure(
const WritableStreamDefaultController* controller) {
// https://streams.spec.whatwg.org/#writable-stream-default-controller-get-backpressure
// 1. Let desiredSize be ! WritableStreamDefaultControllerGetDesiredSize(
// controller).
const double desired_size = GetDesiredSize(controller);
// 2. Return desiredSize ≤ 0.
return desired_size <= 0;
}
void WritableStreamDefaultController::Error(
ScriptState* script_state,
WritableStreamDefaultController* controller,
v8::Local<v8::Value> error) {
// https://streams.spec.whatwg.org/#writable-stream-default-controller-error
// 1. Let stream be controller.[[controlledWritableStream]].
WritableStream* stream = controller->controlled_writable_stream_;
// 2. Assert: stream.[[state]] is "writable".
DCHECK_EQ(stream->GetState(), WritableStream::kWritable);
// 3. Perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
ClearAlgorithms(controller);
// 4. Perform ! WritableStreamStartErroring(stream, error).
WritableStream::StartErroring(script_state, stream, error);
}
} // namespace blink