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"
9 #include "js/PropertyAndElement.h"
10 #include "js/TypeDecls.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"
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(
39 (mGlobal
, mCloseRequest
, mController
, mInFlightWriteRequest
,
40 mInFlightCloseRequest
, mPendingAbortRequestPromise
, mWriter
,
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
)
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
,
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
);
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
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();
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.
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
);
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
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
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
,
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
,
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,
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,
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
,
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).
381 WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer
, aReason
, aRv
);
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
411 RefPtr
<Promise
> promise
= Promise::Create(writer
->GetParentObject(), aRv
);
415 writer
->SetReadyPromise(promise
);
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(
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());
453 // Step 3. If underlyingSinkDict["type"] exists, throw a RangeError exception.
454 if (!underlyingSinkDict
.mType
.isUndefined()) {
455 aRv
.ThrowRangeError("Implementation preserved member 'type'");
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
);
480 // Step 7. Perform ? SetUpWritableStreamDefaultControllerFromUnderlyingSink(
481 // this, underlyingSink, underlyingSinkDict, highWaterMark, sizeAlgorithm).
482 SetUpWritableStreamDefaultControllerFromUnderlyingSink(
483 aGlobal
.Context(), writableStream
, underlyingSinkObj
, underlyingSinkDict
,
484 highWaterMark
, sizeAlgorithm
, aRv
);
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
,
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
);
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
);
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
);
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
);
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
,
580 // Step 1. If ! IsWritableStreamLocked(this) is true, return a promise
581 // rejected with a TypeError exception.
583 RefPtr
<Promise
> promise
= Promise::Create(GetParentObject(), aRv
);
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
,
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
);
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
);
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();
643 // Perform ! WritableStreamDefaultControllerClose(stream.[[controller]]).
644 RefPtr
<WritableStreamDefaultController
> controller
= aStream
->Controller();
645 WritableStreamDefaultControllerClose(aCx
, controller
, aRv
);
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
,
657 // Step 1. If ! IsWritableStreamLocked(this) is true, return a promise
658 // rejected with a TypeError exception.
660 RefPtr
<Promise
> promise
= Promise::Create(GetParentObject(), aRv
);
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
);
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
);
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.
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
);
726 // Step 6: Return stream.
727 return stream
.forget();
730 already_AddRefed
<WritableStreamDefaultWriter
> WritableStream::GetWriter(
732 return AcquireWritableStreamDefaultWriter(this, aRv
);
735 // https://streams.spec.whatwg.org/#writable-stream-add-write-request
736 already_AddRefed
<Promise
> WritableStreamAddWriteRequest(WritableStream
* aStream
,
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
);
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