Bug 1769952 - Fix running raptor on a Win10-64 VM r=sparky
[gecko.git] / dom / streams / WritableStream.cpp
blob89a6616331fe6103090c7dc210cba793703ef461
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/WritableStream.h"
8 #include "js/Array.h"
9 #include "js/PropertyAndElement.h"
10 #include "js/TypeDecls.h"
11 #include "js/Value.h"
12 #include "mozilla/AlreadyAddRefed.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/CycleCollectedJSContext.h"
16 #include "mozilla/FloatingPoint.h"
17 #include "mozilla/HoldDropJSObjects.h"
18 #include "mozilla/dom/AbortSignal.h"
19 #include "mozilla/dom/BindingCallContext.h"
20 #include "mozilla/dom/QueueWithSizes.h"
21 #include "mozilla/dom/QueuingStrategyBinding.h"
22 #include "mozilla/dom/ReadRequest.h"
23 #include "mozilla/dom/RootedDictionary.h"
24 #include "mozilla/dom/StreamUtils.h"
25 #include "mozilla/dom/UnderlyingSinkBinding.h"
26 #include "mozilla/dom/WritableStreamBinding.h"
27 #include "mozilla/dom/WritableStreamDefaultController.h"
28 #include "mozilla/dom/WritableStreamDefaultWriter.h"
29 #include "nsCOMPtr.h"
31 #include "mozilla/dom/Promise-inl.h"
32 #include "nsIGlobalObject.h"
33 #include "nsISupports.h"
35 namespace mozilla::dom {
37 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(
38 WritableStream,
39 (mGlobal, mCloseRequest, mController, mInFlightWriteRequest,
40 mInFlightCloseRequest, mPendingAbortRequestPromise, mWriter,
41 mWriteRequests),
42 (mPendingAbortRequestReason, mStoredError))
44 NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStream)
45 NS_IMPL_CYCLE_COLLECTING_RELEASE(WritableStream)
46 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableStream)
47 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
48 NS_INTERFACE_MAP_ENTRY(nsISupports)
49 NS_INTERFACE_MAP_END
51 WritableStream::WritableStream(nsIGlobalObject* aGlobal) : mGlobal(aGlobal) {
52 mozilla::HoldJSObjects(this);
55 WritableStream::WritableStream(const GlobalObject& aGlobal)
56 : mGlobal(do_QueryInterface(aGlobal.GetAsSupports())) {
57 mozilla::HoldJSObjects(this);
60 WritableStream::~WritableStream() { mozilla::DropJSObjects(this); }
62 JSObject* WritableStream::WrapObject(JSContext* aCx,
63 JS::Handle<JSObject*> aGivenProto) {
64 return WritableStream_Binding::Wrap(aCx, this, aGivenProto);
67 // https://streams.spec.whatwg.org/#writable-stream-deal-with-rejection
68 void WritableStream::DealWithRejection(JSContext* aCx,
69 JS::Handle<JS::Value> aError,
70 ErrorResult& aRv) {
71 // Step 1. Let state be stream.[[state]].
72 // Step 2. If state is "writable",
73 if (mState == WriterState::Writable) {
74 // Step 2.1. Perform ! WritableStreamStartErroring(stream, error).
75 StartErroring(aCx, aError, aRv);
77 // Step 2.2. Return.
78 return;
81 // Step 3. Assert: state is "erroring".
82 MOZ_ASSERT(mState == WriterState::Erroring);
84 // Step 4. Perform ! WritableStreamFinishErroring(stream).
85 FinishErroring(aCx, aRv);
88 // https://streams.spec.whatwg.org/#writable-stream-finish-erroring
89 void WritableStream::FinishErroring(JSContext* aCx, ErrorResult& aRv) {
90 // Step 1. Assert: stream.[[state]] is "erroring".
91 MOZ_ASSERT(mState == WriterState::Erroring);
93 // Step 2. Assert: ! WritableStreamHasOperationMarkedInFlight(stream) is
94 // false.
95 MOZ_ASSERT(!HasOperationMarkedInFlight());
97 // Step 3. Set stream.[[state]] to "errored".
98 mState = WriterState::Errored;
100 // Step 4. Perform ! stream.[[controller]].[[ErrorSteps]]().
101 Controller()->ErrorSteps();
103 // Step 5. Let storedError be stream.[[storedError]].
104 JS::Rooted<JS::Value> storedError(aCx, mStoredError);
106 // Step 6. For each writeRequest of stream.[[writeRequests]]:
107 for (const RefPtr<Promise>& writeRequest : mWriteRequests) {
108 // Step 6.1. Reject writeRequest with storedError.
109 writeRequest->MaybeReject(storedError);
112 // Step 7. Set stream.[[writeRequests]] to an empty list.
113 mWriteRequests.Clear();
115 // Step 8. If stream.[[pendingAbortRequest]] is undefined,
116 if (!mPendingAbortRequestPromise) {
117 // Step 8.1. Perform !
118 // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
119 RejectCloseAndClosedPromiseIfNeeded();
121 // Step 8.2. Return.
122 return;
125 // Step 9. Let abortRequest be stream.[[pendingAbortRequest]].
126 RefPtr<Promise> abortPromise = mPendingAbortRequestPromise;
127 JS::Rooted<JS::Value> abortReason(aCx, mPendingAbortRequestReason);
128 bool abortWasAlreadyErroring = mPendingAbortRequestWasAlreadyErroring;
130 // Step 10. Set stream.[[pendingAbortRequest]] to undefined.
131 SetPendingAbortRequest(nullptr, JS::UndefinedHandleValue, false);
133 // Step 11. If abortRequest’s was already erroring is true,
134 if (abortWasAlreadyErroring) {
135 // Step 11.1. Reject abortRequest’s promise with storedError.
136 abortPromise->MaybeReject(storedError);
138 // Step 11.2. Perform !
139 // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
140 RejectCloseAndClosedPromiseIfNeeded();
142 // Step 11.3. Return.
143 return;
146 // Step 12. Let promise be !
147 // stream.[[controller]].[[AbortSteps]](abortRequest’s reason).
148 RefPtr<WritableStreamDefaultController> controller = mController;
149 RefPtr<Promise> promise = controller->AbortSteps(aCx, abortReason, aRv);
150 if (aRv.Failed()) {
151 return;
154 // Step 13 + 14.
155 promise->AddCallbacksWithCycleCollectedArgs(
156 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
157 Promise* aAbortRequestPromise, WritableStream* aStream) {
158 // Step 13. Upon fulfillment of promise,
159 // Step 13.1. Resolve abortRequest’s promise with undefined.
160 aAbortRequestPromise->MaybeResolveWithUndefined();
162 // Step 13.2. Perform !
163 // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
164 aStream->RejectCloseAndClosedPromiseIfNeeded();
166 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
167 Promise* aAbortRequestPromise, WritableStream* aStream) {
168 // Step 14. Upon rejection of promise with reason reason,
169 // Step 14.1. Reject abortRequest’s promise with reason.
170 aAbortRequestPromise->MaybeReject(aValue);
172 // Step 14.2. Perform !
173 // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
174 aStream->RejectCloseAndClosedPromiseIfNeeded();
176 RefPtr(abortPromise), RefPtr(this));
179 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close
180 void WritableStream::FinishInFlightClose() {
181 // Step 1. Assert: stream.[[inFlightCloseRequest]] is not undefined.
182 MOZ_ASSERT(mInFlightCloseRequest);
184 // Step 2. Resolve stream.[[inFlightCloseRequest]] with undefined.
185 mInFlightCloseRequest->MaybeResolveWithUndefined();
187 // Step 3. Set stream.[[inFlightCloseRequest]] to undefined.
188 mInFlightCloseRequest = nullptr;
190 // Step 4. Let state be stream.[[state]].
191 // Step 5. Assert: stream.[[state]] is "writable" or "erroring".
192 MOZ_ASSERT(mState == WriterState::Writable ||
193 mState == WriterState::Erroring);
195 // Step 6. If state is "erroring",
196 if (mState == WriterState::Erroring) {
197 // Step 6.1. Set stream.[[storedError]] to undefined.
198 mStoredError.setUndefined();
200 // Step 6.2. If stream.[[pendingAbortRequest]] is not undefined,
201 if (mPendingAbortRequestPromise) {
202 // Step 6.2.1. Resolve stream.[[pendingAbortRequest]]'s promise with
203 // undefined.
204 mPendingAbortRequestPromise->MaybeResolveWithUndefined();
206 // Step 6.2.2. Set stream.[[pendingAbortRequest]] to undefined.
207 SetPendingAbortRequest(nullptr, JS::UndefinedHandleValue, false);
211 // Step 7. Set stream.[[state]] to "closed".
212 mState = WriterState::Closed;
214 // Step 8. Let writer be stream.[[writer]].
215 // Step 9. If writer is not undefined, resolve writer.[[closedPromise]] with
216 // undefined.
217 if (mWriter) {
218 mWriter->ClosedPromise()->MaybeResolveWithUndefined();
221 // Step 10. Assert: stream.[[pendingAbortRequest]] is undefined.
222 MOZ_ASSERT(!mPendingAbortRequestPromise);
223 // Assert: stream.[[storedError]] is undefined.
224 MOZ_ASSERT(mStoredError.isUndefined());
227 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close-with-error
228 void WritableStream::FinishInFlightCloseWithError(JSContext* aCx,
229 JS::Handle<JS::Value> aError,
230 ErrorResult& aRv) {
231 // Step 1. Assert: stream.[[inFlightCloseRequest]] is not undefined.
232 MOZ_ASSERT(mInFlightCloseRequest);
234 // Step 2. Reject stream.[[inFlightCloseRequest]] with error.
235 mInFlightCloseRequest->MaybeReject(aError);
237 // Step 3. Set stream.[[inFlightCloseRequest]] to undefined.
238 mInFlightCloseRequest = nullptr;
240 // Step 4. Assert: stream.[[state]] is "writable" or "erroring".
241 MOZ_ASSERT(mState == WriterState::Writable ||
242 mState == WriterState::Erroring);
244 // Step 5. If stream.[[pendingAbortRequest]] is not undefined,
245 if (mPendingAbortRequestPromise) {
246 // Step 5.1. Reject stream.[[pendingAbortRequest]]'s promise with error.
247 mPendingAbortRequestPromise->MaybeReject(aError);
249 // Step 5.2. Set stream.[[pendingAbortRequest]] to undefined.
250 SetPendingAbortRequest(nullptr, JS::UndefinedHandleValue, false);
253 // Step 6. Perform ! WritableStreamDealWithRejection(stream, error).
254 DealWithRejection(aCx, aError, aRv);
257 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write
258 void WritableStream::FinishInFlightWrite() {
259 // Step 1. Assert: stream.[[inFlightWriteRequest]] is not undefined.
260 MOZ_ASSERT(mInFlightWriteRequest);
262 // Step 2. Resolve stream.[[inFlightWriteRequest]] with undefined.
263 mInFlightWriteRequest->MaybeResolveWithUndefined();
265 // Step 3. Set stream.[[inFlightWriteRequest]] to undefined.
266 mInFlightWriteRequest = nullptr;
269 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write-with-error
270 void WritableStream::FinishInFlightWriteWithError(JSContext* aCx,
271 JS::Handle<JS::Value> aError,
272 ErrorResult& aRv) {
273 // Step 1. Assert: stream.[[inFlightWriteRequest]] is not undefined.
274 MOZ_ASSERT(mInFlightWriteRequest);
276 // Step 2. Reject stream.[[inFlightWriteRequest]] with error.
277 mInFlightWriteRequest->MaybeReject(aError);
279 // Step 3. Set stream.[[inFlightWriteRequest]] to undefined.
280 mInFlightWriteRequest = nullptr;
282 // Step 4. Assert: stream.[[state]] is "writable" or "erroring".
283 MOZ_ASSERT(mState == WriterState::Writable ||
284 mState == WriterState::Erroring);
286 // Step 5. Perform ! WritableStreamDealWithRejection(stream, error).
287 DealWithRejection(aCx, aError, aRv);
290 // https://streams.spec.whatwg.org/#writable-stream-mark-close-request-in-flight
291 void WritableStream::MarkCloseRequestInFlight() {
292 // Step 1. Assert: stream.[[inFlightCloseRequest]] is undefined.
293 MOZ_ASSERT(!mInFlightCloseRequest);
295 // Step 2. Assert: stream.[[closeRequest]] is not undefined.
296 MOZ_ASSERT(mCloseRequest);
298 // Step 3. Set stream.[[inFlightCloseRequest]] to stream.[[closeRequest]].
299 mInFlightCloseRequest = mCloseRequest;
301 // Step 4. Set stream.[[closeRequest]] to undefined.
302 mCloseRequest = nullptr;
305 // https://streams.spec.whatwg.org/#writable-stream-mark-first-write-request-in-flight
306 void WritableStream::MarkFirstWriteRequestInFlight() {
307 // Step 1. Assert: stream.[[inFlightWriteRequest]] is undefined.
308 MOZ_ASSERT(!mInFlightWriteRequest);
310 // Step 2. Assert: stream.[[writeRequests]] is not empty.
311 MOZ_ASSERT(!mWriteRequests.IsEmpty());
313 // Step 3. Let writeRequest be stream.[[writeRequests]][0].
314 RefPtr<Promise> writeRequest = mWriteRequests.ElementAt(0);
316 // Step 4. Remove writeRequest from stream.[[writeRequests]].
317 mWriteRequests.RemoveElementAt(0);
319 // Step 5. Set stream.[[inFlightWriteRequest]] to writeRequest.
320 mInFlightWriteRequest = writeRequest;
323 // https://streams.spec.whatwg.org/#writable-stream-reject-close-and-closed-promise-if-needed
324 void WritableStream::RejectCloseAndClosedPromiseIfNeeded() {
325 // Step 1. Assert: stream.[[state]] is "errored".
326 MOZ_ASSERT(mState == WriterState::Errored);
328 JS::Rooted<JS::Value> storedError(RootingCx(), mStoredError);
329 // Step 2. If stream.[[closeRequest]] is not undefined,
330 if (mCloseRequest) {
331 // Step 2.1. Assert: stream.[[inFlightCloseRequest]] is undefined.
332 MOZ_ASSERT(!mInFlightCloseRequest);
334 // Step 2.2. Reject stream.[[closeRequest]] with stream.[[storedError]].
335 mCloseRequest->MaybeReject(storedError);
337 // Step 2.3. Set stream.[[closeRequest]] to undefined.
338 mCloseRequest = nullptr;
341 // Step 3. Let writer be stream.[[writer]].
342 RefPtr<WritableStreamDefaultWriter> writer = mWriter;
344 // Step 4. If writer is not undefined,
345 if (writer) {
346 // Step 4.1. Reject writer.[[closedPromise]] with stream.[[storedError]].
347 RefPtr<Promise> closedPromise = writer->ClosedPromise();
348 closedPromise->MaybeReject(storedError);
350 // Step 4.2. Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
351 closedPromise->SetSettledPromiseIsHandled();
355 // https://streams.spec.whatwg.org/#writable-stream-start-erroring
356 void WritableStream::StartErroring(JSContext* aCx,
357 JS::Handle<JS::Value> aReason,
358 ErrorResult& aRv) {
359 // Step 1. Assert: stream.[[storedError]] is undefined.
360 MOZ_ASSERT(mStoredError.isUndefined());
362 // Step 2. Assert: stream.[[state]] is "writable".
363 MOZ_ASSERT(mState == WriterState::Writable);
365 // Step 3. Let controller be stream.[[controller]].
366 RefPtr<WritableStreamDefaultController> controller = mController;
367 // Step 4. Assert: controller is not undefined.
368 MOZ_ASSERT(controller);
370 // Step 5. Set stream.[[state]] to "erroring".
371 mState = WriterState::Erroring;
373 // Step 6. Set stream.[[storedError]] to reason.
374 mStoredError = aReason;
376 // Step 7. Let writer be stream.[[writer]].
377 RefPtr<WritableStreamDefaultWriter> writer = mWriter;
378 // Step 8. If writer is not undefined, perform !
379 // WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason).
380 if (writer) {
381 WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, aReason, aRv);
382 if (aRv.Failed()) {
383 return;
387 // Step 9. If ! WritableStreamHasOperationMarkedInFlight(stream) is false
388 // and controller.[[started]] is true,
389 // perform !WritableStreamFinishErroring(stream).
390 if (!HasOperationMarkedInFlight() && controller->Started()) {
391 FinishErroring(aCx, aRv);
395 // https://streams.spec.whatwg.org/#writable-stream-update-backpressure
396 void WritableStream::UpdateBackpressure(bool aBackpressure, ErrorResult& aRv) {
397 // Step 1. Assert: stream.[[state]] is "writable".
398 MOZ_ASSERT(mState == WriterState::Writable);
399 // Step 2. Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false.
400 MOZ_ASSERT(!CloseQueuedOrInFlight());
402 // Step 3. Let writer be stream.[[writer]].
403 RefPtr<WritableStreamDefaultWriter> writer = mWriter;
405 // Step 4. If writer is not undefined and backpressure is not
406 // stream.[[backpressure]],
407 if (writer && aBackpressure != mBackpressure) {
408 // Step 4.1. If backpressure is true, set writer.[[readyPromise]] to a new
409 // promise.
410 if (aBackpressure) {
411 RefPtr<Promise> promise = Promise::Create(writer->GetParentObject(), aRv);
412 if (aRv.Failed()) {
413 return;
415 writer->SetReadyPromise(promise);
416 } else {
417 // Step 4.2. Otherwise,
418 // Step 4.2.1. Assert: backpressure is false.
419 // Step 4.2.2. Resolve writer.[[readyPromise]] with undefined.
420 writer->ReadyPromise()->MaybeResolveWithUndefined();
424 // Step 5. Set stream.[[backpressure]] to backpressure.
425 mBackpressure = aBackpressure;
428 // https://streams.spec.whatwg.org/#ws-constructor
429 already_AddRefed<WritableStream> WritableStream::Constructor(
430 const GlobalObject& aGlobal,
431 const Optional<JS::Handle<JSObject*>>& aUnderlyingSink,
432 const QueuingStrategy& aStrategy, ErrorResult& aRv) {
433 // Step 1. If underlyingSink is missing, set it to null.
434 JS::Rooted<JSObject*> underlyingSinkObj(
435 aGlobal.Context(),
436 aUnderlyingSink.WasPassed() ? aUnderlyingSink.Value() : nullptr);
438 // Step 2. Let underlyingSinkDict be underlyingSink, converted to
439 // an IDL value of type UnderlyingSink.
440 RootedDictionary<UnderlyingSink> underlyingSinkDict(aGlobal.Context());
441 if (underlyingSinkObj) {
442 JS::Rooted<JS::Value> objValue(aGlobal.Context(),
443 JS::ObjectValue(*underlyingSinkObj));
444 dom::BindingCallContext callCx(aGlobal.Context(),
445 "WritableStream.constructor");
446 aRv.MightThrowJSException();
447 if (!underlyingSinkDict.Init(callCx, objValue)) {
448 aRv.StealExceptionFromJSContext(aGlobal.Context());
449 return nullptr;
453 // Step 3. If underlyingSinkDict["type"] exists, throw a RangeError exception.
454 if (!underlyingSinkDict.mType.isUndefined()) {
455 aRv.ThrowRangeError("Implementation preserved member 'type'");
456 return nullptr;
459 // Step 4. Perform ! InitializeWritableStream(this).
460 RefPtr<WritableStream> writableStream = new WritableStream(aGlobal);
462 // Step 5. Let sizeAlgorithm be ! ExtractSizeAlgorithm(strategy).
464 // Implementation Note: The specification demands that if the size doesn't
465 // exist, we instead would provide an algorithm that returns 1. Instead, we
466 // will teach callers that a missing callback should simply return 1, rather
467 // than gin up a fake callback here.
469 // This decision may need to be revisited if the default action ever diverges
470 // within the specification.
471 RefPtr<QueuingStrategySize> sizeAlgorithm =
472 aStrategy.mSize.WasPassed() ? &aStrategy.mSize.Value() : nullptr;
474 // Step 6. Let highWaterMark be ? ExtractHighWaterMark(strategy, 1).
475 double highWaterMark = ExtractHighWaterMark(aStrategy, 1, aRv);
476 if (aRv.Failed()) {
477 return nullptr;
480 // Step 7. Perform ? SetUpWritableStreamDefaultControllerFromUnderlyingSink(
481 // this, underlyingSink, underlyingSinkDict, highWaterMark, sizeAlgorithm).
482 SetUpWritableStreamDefaultControllerFromUnderlyingSink(
483 aGlobal.Context(), writableStream, underlyingSinkObj, underlyingSinkDict,
484 highWaterMark, sizeAlgorithm, aRv);
485 if (aRv.Failed()) {
486 return nullptr;
489 return writableStream.forget();
492 // https://streams.spec.whatwg.org/#writable-stream-abort
493 already_AddRefed<Promise> WritableStreamAbort(JSContext* aCx,
494 WritableStream* aStream,
495 JS::Handle<JS::Value> aReason,
496 ErrorResult& aRv) {
497 // Step 1. If stream.[[state]] is "closed" or "errored", return a promise
498 // resolved with undefined.
499 if (aStream->State() == WritableStream::WriterState::Closed ||
500 aStream->State() == WritableStream::WriterState::Errored) {
501 RefPtr<Promise> promise = Promise::Create(aStream->GetParentObject(), aRv);
502 if (aRv.Failed()) {
503 return nullptr;
505 promise->MaybeResolveWithUndefined();
506 return promise.forget();
509 // Step 2. Signal abort on stream.[[controller]].[[signal]] with reason.
510 RefPtr<WritableStreamDefaultController> controller = aStream->Controller();
511 controller->Signal()->SignalAbort(aReason);
513 // Step 3. Let state be stream.[[state]].
514 WritableStream::WriterState state = aStream->State();
516 // Step 4. If state is "closed" or "errored", return a promise resolved with
517 // undefined. Note: We re-check the state because signaling abort runs author
518 // code and that might have changed the state.
519 if (aStream->State() == WritableStream::WriterState::Closed ||
520 aStream->State() == WritableStream::WriterState::Errored) {
521 RefPtr<Promise> promise = Promise::Create(aStream->GetParentObject(), aRv);
522 if (aRv.Failed()) {
523 return nullptr;
525 promise->MaybeResolveWithUndefined();
526 return promise.forget();
529 // Step 5. If stream.[[pendingAbortRequest]] is not undefined, return
530 // stream.[[pendingAbortRequest]]'s promise.
531 if (aStream->GetPendingAbortRequestPromise()) {
532 RefPtr<Promise> promise = aStream->GetPendingAbortRequestPromise();
533 return promise.forget();
536 // Step 6. Assert: state is "writable" or "erroring".
537 MOZ_ASSERT(state == WritableStream::WriterState::Writable ||
538 state == WritableStream::WriterState::Erroring);
540 // Step 7. Let wasAlreadyErroring be false.
541 bool wasAlreadyErroring = false;
543 // Step 8. If state is "erroring",
544 JS::Rooted<JS::Value> reason(aCx, aReason);
545 if (state == WritableStream::WriterState::Erroring) {
546 // Step 8.1. Set wasAlreadyErroring to true.
547 wasAlreadyErroring = true;
548 // Step 8.2. Set reason to undefined.
549 reason.setUndefined();
552 // Step 9. Let promise be a new promise.
553 RefPtr<Promise> promise = Promise::Create(aStream->GetParentObject(), aRv);
554 if (aRv.Failed()) {
555 return nullptr;
558 // Step 10. Set stream.[[pendingAbortRequest]] to a new pending abort request
559 // whose promise is promise, reason is reason, and was already erroring is
560 // wasAlreadyErroring.
561 aStream->SetPendingAbortRequest(promise, reason, wasAlreadyErroring);
563 // Step 11. If wasAlreadyErroring is false, perform !
564 // WritableStreamStartErroring(stream, reason).
565 if (!wasAlreadyErroring) {
566 aStream->StartErroring(aCx, reason, aRv);
567 if (aRv.Failed()) {
568 return nullptr;
572 // Step 12. Return promise.
573 return promise.forget();
576 // https://streams.spec.whatwg.org/#ws-abort
577 already_AddRefed<Promise> WritableStream::Abort(JSContext* aCx,
578 JS::Handle<JS::Value> aReason,
579 ErrorResult& aRv) {
580 // Step 1. If ! IsWritableStreamLocked(this) is true, return a promise
581 // rejected with a TypeError exception.
582 if (Locked()) {
583 RefPtr<Promise> promise = Promise::Create(GetParentObject(), aRv);
584 if (aRv.Failed()) {
585 return nullptr;
587 promise->MaybeRejectWithTypeError("Canceled Locked Stream");
588 return promise.forget();
591 // Step 2. Return ! WritableStreamAbort(this, reason).
592 RefPtr<WritableStream> thisRefPtr = this;
593 return WritableStreamAbort(aCx, thisRefPtr, aReason, aRv);
596 // https://streams.spec.whatwg.org/#writable-stream-close
597 already_AddRefed<Promise> WritableStreamClose(JSContext* aCx,
598 WritableStream* aStream,
599 ErrorResult& aRv) {
600 // Step 1. Let state be stream.[[state]].
601 WritableStream::WriterState state = aStream->State();
603 // Step 2. If state is "closed" or "errored", return a promise rejected with a
604 // TypeError exception.
605 if (state == WritableStream::WriterState::Closed ||
606 state == WritableStream::WriterState::Errored) {
607 RefPtr<Promise> promise = Promise::Create(aStream->GetParentObject(), aRv);
608 if (aRv.Failed()) {
609 return nullptr;
611 promise->MaybeRejectWithTypeError(
612 "Can not close stream after closing or error");
613 return promise.forget();
616 // Step 3. Assert: state is "writable" or "erroring".
617 MOZ_ASSERT(state == WritableStream::WriterState::Writable ||
618 state == WritableStream::WriterState::Erroring);
620 // Step 4. Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false.
621 MOZ_ASSERT(!aStream->CloseQueuedOrInFlight());
623 // Step 5. Let promise be a new promise.
624 RefPtr<Promise> promise = Promise::Create(aStream->GetParentObject(), aRv);
625 if (aRv.Failed()) {
626 return nullptr;
629 // Step 6. Set stream.[[closeRequest]] to promise.
630 aStream->SetCloseRequest(promise);
632 // Step 7. Let writer be stream.[[writer]].
633 RefPtr<WritableStreamDefaultWriter> writer = aStream->GetWriter();
635 // Step 8. If writer is not undefined, and stream.[[backpressure]] is true,
636 // and state is "writable", resolve writer.[[readyPromise]] with undefined.
637 if (writer && aStream->Backpressure() &&
638 state == WritableStream::WriterState::Writable) {
639 writer->ReadyPromise()->MaybeResolveWithUndefined();
642 // Step 9.
643 // Perform ! WritableStreamDefaultControllerClose(stream.[[controller]]).
644 RefPtr<WritableStreamDefaultController> controller = aStream->Controller();
645 WritableStreamDefaultControllerClose(aCx, controller, aRv);
646 if (aRv.Failed()) {
647 return nullptr;
650 // Step 10. Return promise.
651 return promise.forget();
654 // https://streams.spec.whatwg.org/#ws-close
655 already_AddRefed<Promise> WritableStream::Close(JSContext* aCx,
656 ErrorResult& aRv) {
657 // Step 1. If ! IsWritableStreamLocked(this) is true, return a promise
658 // rejected with a TypeError exception.
659 if (Locked()) {
660 RefPtr<Promise> promise = Promise::Create(GetParentObject(), aRv);
661 if (aRv.Failed()) {
662 return nullptr;
664 promise->MaybeRejectWithTypeError("Can not close locked stream");
665 return promise.forget();
668 // Step 2. If ! WritableStreamCloseQueuedOrInFlight(this) is true, return a
669 // promise rejected with a TypeError exception.
670 if (CloseQueuedOrInFlight()) {
671 RefPtr<Promise> promise = Promise::Create(GetParentObject(), aRv);
672 if (aRv.Failed()) {
673 return nullptr;
675 promise->MaybeRejectWithTypeError("Stream is already closing");
676 return promise.forget();
679 // Step 3. Return ! WritableStreamClose(this).
680 RefPtr<WritableStream> thisRefPtr = this;
681 return WritableStreamClose(aCx, thisRefPtr, aRv);
684 // https://streams.spec.whatwg.org/#acquire-writable-stream-default-writer
685 already_AddRefed<WritableStreamDefaultWriter>
686 AcquireWritableStreamDefaultWriter(WritableStream* aStream, ErrorResult& aRv) {
687 // Step 1. Let writer be a new WritableStreamDefaultWriter.
688 RefPtr<WritableStreamDefaultWriter> writer =
689 new WritableStreamDefaultWriter(aStream->GetParentObject());
691 // Step 2. Perform ? SetUpWritableStreamDefaultWriter(writer, stream).
692 SetUpWritableStreamDefaultWriter(writer, aStream, aRv);
693 if (aRv.Failed()) {
694 return nullptr;
697 // Step 3. Return writer.
698 return writer.forget();
701 // https://streams.spec.whatwg.org/#create-writable-stream
702 already_AddRefed<WritableStream> CreateWritableStream(
703 JSContext* aCx, nsIGlobalObject* aGlobal,
704 UnderlyingSinkAlgorithmsBase* aAlgorithms, double aHighWaterMark,
705 QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) {
706 // Step 1: Assert: ! IsNonNegativeNumber(highWaterMark) is true.
707 MOZ_ASSERT(IsNonNegativeNumber(aHighWaterMark));
709 // Step 2: Let stream be a new WritableStream.
710 // Step 3: Perform ! InitializeWritableStream(stream).
711 auto stream = MakeRefPtr<WritableStream>(aGlobal);
713 // Step 4: Let controller be a new WritableStreamDefaultController.
714 auto controller =
715 MakeRefPtr<WritableStreamDefaultController>(aGlobal, *stream);
717 // Step 5: Perform ? SetUpWritableStreamDefaultController(stream, controller,
718 // startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm,
719 // highWaterMark, sizeAlgorithm).
720 SetUpWritableStreamDefaultController(aCx, stream, controller, aAlgorithms,
721 aHighWaterMark, aSizeAlgorithm, aRv);
722 if (aRv.Failed()) {
723 return nullptr;
726 // Step 6: Return stream.
727 return stream.forget();
730 already_AddRefed<WritableStreamDefaultWriter> WritableStream::GetWriter(
731 ErrorResult& aRv) {
732 return AcquireWritableStreamDefaultWriter(this, aRv);
735 // https://streams.spec.whatwg.org/#writable-stream-add-write-request
736 already_AddRefed<Promise> WritableStreamAddWriteRequest(WritableStream* aStream,
737 ErrorResult& aRv) {
738 // Step 1. Assert: ! IsWritableStreamLocked(stream) is true.
739 MOZ_ASSERT(IsWritableStreamLocked(aStream));
741 // Step 2. Assert: stream.[[state]] is "writable".
742 MOZ_ASSERT(aStream->State() == WritableStream::WriterState::Writable);
744 // Step 3. Let promise be a new promise.
745 RefPtr<Promise> promise = Promise::Create(aStream->GetParentObject(), aRv);
746 if (aRv.Failed()) {
747 return nullptr;
750 // Step 4. Append promise to stream.[[writeRequests]].
751 aStream->AppendWriteRequest(promise);
753 // Step 5. Return promise.
754 return promise.forget();
757 } // namespace mozilla::dom