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 "StreamUtils.h"
11 #include "js/PropertyAndElement.h"
12 #include "js/TypeDecls.h"
14 #include "mozilla/AlreadyAddRefed.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/Attributes.h"
17 #include "mozilla/CycleCollectedJSContext.h"
18 #include "mozilla/HoldDropJSObjects.h"
19 #include "mozilla/dom/AbortSignal.h"
20 #include "mozilla/dom/BindingCallContext.h"
21 #include "mozilla/dom/QueueWithSizes.h"
22 #include "mozilla/dom/QueuingStrategyBinding.h"
23 #include "mozilla/dom/ReadRequest.h"
24 #include "mozilla/dom/RootedDictionary.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 using namespace streams_abstract
;
39 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS(
41 (mGlobal
, mCloseRequest
, mController
, mInFlightWriteRequest
,
42 mInFlightCloseRequest
, mPendingAbortRequestPromise
, mWriter
,
44 (mPendingAbortRequestReason
, mStoredError
))
46 NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStream
)
47 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(WritableStream
,
49 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableStream
)
50 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
51 NS_INTERFACE_MAP_ENTRY(nsISupports
)
54 WritableStream::WritableStream(nsIGlobalObject
* aGlobal
,
55 HoldDropJSObjectsCaller aHoldDropCaller
)
56 : mGlobal(aGlobal
), mHoldDropCaller(aHoldDropCaller
) {
57 if (mHoldDropCaller
== HoldDropJSObjectsCaller::Implicit
) {
58 mozilla::HoldJSObjects(this);
62 WritableStream::WritableStream(const GlobalObject
& aGlobal
,
63 HoldDropJSObjectsCaller aHoldDropCaller
)
64 : mGlobal(do_QueryInterface(aGlobal
.GetAsSupports())),
65 mHoldDropCaller(aHoldDropCaller
) {
66 if (mHoldDropCaller
== HoldDropJSObjectsCaller::Implicit
) {
67 mozilla::HoldJSObjects(this);
71 WritableStream::~WritableStream() {
72 if (mHoldDropCaller
== HoldDropJSObjectsCaller::Implicit
) {
73 mozilla::DropJSObjects(this);
77 JSObject
* WritableStream::WrapObject(JSContext
* aCx
,
78 JS::Handle
<JSObject
*> aGivenProto
) {
79 return WritableStream_Binding::Wrap(aCx
, this, aGivenProto
);
82 // https://streams.spec.whatwg.org/#writable-stream-deal-with-rejection
83 void WritableStream::DealWithRejection(JSContext
* aCx
,
84 JS::Handle
<JS::Value
> aError
,
86 // Step 1. Let state be stream.[[state]].
87 // Step 2. If state is "writable",
88 if (mState
== WriterState::Writable
) {
89 // Step 2.1. Perform ! WritableStreamStartErroring(stream, error).
90 StartErroring(aCx
, aError
, aRv
);
96 // Step 3. Assert: state is "erroring".
97 MOZ_ASSERT(mState
== WriterState::Erroring
);
99 // Step 4. Perform ! WritableStreamFinishErroring(stream).
100 FinishErroring(aCx
, aRv
);
103 // https://streams.spec.whatwg.org/#writable-stream-finish-erroring
104 void WritableStream::FinishErroring(JSContext
* aCx
, ErrorResult
& aRv
) {
105 // Step 1. Assert: stream.[[state]] is "erroring".
106 MOZ_ASSERT(mState
== WriterState::Erroring
);
108 // Step 2. Assert: ! WritableStreamHasOperationMarkedInFlight(stream) is
110 MOZ_ASSERT(!HasOperationMarkedInFlight());
112 // Step 3. Set stream.[[state]] to "errored".
113 mState
= WriterState::Errored
;
115 // Step 4. Perform ! stream.[[controller]].[[ErrorSteps]]().
116 Controller()->ErrorSteps();
118 // Step 5. Let storedError be stream.[[storedError]].
119 JS::Rooted
<JS::Value
> storedError(aCx
, mStoredError
);
121 // Step 6. For each writeRequest of stream.[[writeRequests]]:
122 for (const RefPtr
<Promise
>& writeRequest
: mWriteRequests
) {
123 // Step 6.1. Reject writeRequest with storedError.
124 writeRequest
->MaybeReject(storedError
);
127 // Step 7. Set stream.[[writeRequests]] to an empty list.
128 mWriteRequests
.Clear();
130 // Step 8. If stream.[[pendingAbortRequest]] is undefined,
131 if (!mPendingAbortRequestPromise
) {
132 // Step 8.1. Perform !
133 // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
134 RejectCloseAndClosedPromiseIfNeeded();
140 // Step 9. Let abortRequest be stream.[[pendingAbortRequest]].
141 RefPtr
<Promise
> abortPromise
= mPendingAbortRequestPromise
;
142 JS::Rooted
<JS::Value
> abortReason(aCx
, mPendingAbortRequestReason
);
143 bool abortWasAlreadyErroring
= mPendingAbortRequestWasAlreadyErroring
;
145 // Step 10. Set stream.[[pendingAbortRequest]] to undefined.
146 SetPendingAbortRequest(nullptr, JS::UndefinedHandleValue
, false);
148 // Step 11. If abortRequest’s was already erroring is true,
149 if (abortWasAlreadyErroring
) {
150 // Step 11.1. Reject abortRequest’s promise with storedError.
151 abortPromise
->MaybeReject(storedError
);
153 // Step 11.2. Perform !
154 // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
155 RejectCloseAndClosedPromiseIfNeeded();
157 // Step 11.3. Return.
161 // Step 12. Let promise be !
162 // stream.[[controller]].[[AbortSteps]](abortRequest’s reason).
163 RefPtr
<WritableStreamDefaultController
> controller
= mController
;
164 RefPtr
<Promise
> promise
= controller
->AbortSteps(aCx
, abortReason
, aRv
);
170 promise
->AddCallbacksWithCycleCollectedArgs(
171 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
,
172 Promise
* aAbortRequestPromise
, WritableStream
* aStream
) {
173 // Step 13. Upon fulfillment of promise,
174 // Step 13.1. Resolve abortRequest’s promise with undefined.
175 aAbortRequestPromise
->MaybeResolveWithUndefined();
177 // Step 13.2. Perform !
178 // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
179 aStream
->RejectCloseAndClosedPromiseIfNeeded();
181 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
,
182 Promise
* aAbortRequestPromise
, WritableStream
* aStream
) {
183 // Step 14. Upon rejection of promise with reason reason,
184 // Step 14.1. Reject abortRequest’s promise with reason.
185 aAbortRequestPromise
->MaybeReject(aValue
);
187 // Step 14.2. Perform !
188 // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
189 aStream
->RejectCloseAndClosedPromiseIfNeeded();
191 RefPtr(abortPromise
), RefPtr(this));
194 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close
195 void WritableStream::FinishInFlightClose() {
196 // Step 1. Assert: stream.[[inFlightCloseRequest]] is not undefined.
197 MOZ_ASSERT(mInFlightCloseRequest
);
199 // Step 2. Resolve stream.[[inFlightCloseRequest]] with undefined.
200 mInFlightCloseRequest
->MaybeResolveWithUndefined();
202 // Step 3. Set stream.[[inFlightCloseRequest]] to undefined.
203 mInFlightCloseRequest
= nullptr;
205 // Step 4. Let state be stream.[[state]].
206 // Step 5. Assert: stream.[[state]] is "writable" or "erroring".
207 MOZ_ASSERT(mState
== WriterState::Writable
||
208 mState
== WriterState::Erroring
);
210 // Step 6. If state is "erroring",
211 if (mState
== WriterState::Erroring
) {
212 // Step 6.1. Set stream.[[storedError]] to undefined.
213 mStoredError
.setUndefined();
215 // Step 6.2. If stream.[[pendingAbortRequest]] is not undefined,
216 if (mPendingAbortRequestPromise
) {
217 // Step 6.2.1. Resolve stream.[[pendingAbortRequest]]'s promise with
219 mPendingAbortRequestPromise
->MaybeResolveWithUndefined();
221 // Step 6.2.2. Set stream.[[pendingAbortRequest]] to undefined.
222 SetPendingAbortRequest(nullptr, JS::UndefinedHandleValue
, false);
226 // Step 7. Set stream.[[state]] to "closed".
227 mState
= WriterState::Closed
;
229 // Step 8. Let writer be stream.[[writer]].
230 // Step 9. If writer is not undefined, resolve writer.[[closedPromise]] with
233 mWriter
->ClosedPromise()->MaybeResolveWithUndefined();
236 // Step 10. Assert: stream.[[pendingAbortRequest]] is undefined.
237 MOZ_ASSERT(!mPendingAbortRequestPromise
);
238 // Assert: stream.[[storedError]] is undefined.
239 MOZ_ASSERT(mStoredError
.isUndefined());
242 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close-with-error
243 void WritableStream::FinishInFlightCloseWithError(JSContext
* aCx
,
244 JS::Handle
<JS::Value
> aError
,
246 // Step 1. Assert: stream.[[inFlightCloseRequest]] is not undefined.
247 MOZ_ASSERT(mInFlightCloseRequest
);
249 // Step 2. Reject stream.[[inFlightCloseRequest]] with error.
250 mInFlightCloseRequest
->MaybeReject(aError
);
252 // Step 3. Set stream.[[inFlightCloseRequest]] to undefined.
253 mInFlightCloseRequest
= nullptr;
255 // Step 4. Assert: stream.[[state]] is "writable" or "erroring".
256 MOZ_ASSERT(mState
== WriterState::Writable
||
257 mState
== WriterState::Erroring
);
259 // Step 5. If stream.[[pendingAbortRequest]] is not undefined,
260 if (mPendingAbortRequestPromise
) {
261 // Step 5.1. Reject stream.[[pendingAbortRequest]]'s promise with error.
262 mPendingAbortRequestPromise
->MaybeReject(aError
);
264 // Step 5.2. Set stream.[[pendingAbortRequest]] to undefined.
265 SetPendingAbortRequest(nullptr, JS::UndefinedHandleValue
, false);
268 // Step 6. Perform ! WritableStreamDealWithRejection(stream, error).
269 DealWithRejection(aCx
, aError
, aRv
);
272 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write
273 void WritableStream::FinishInFlightWrite() {
274 // Step 1. Assert: stream.[[inFlightWriteRequest]] is not undefined.
275 MOZ_ASSERT(mInFlightWriteRequest
);
277 // Step 2. Resolve stream.[[inFlightWriteRequest]] with undefined.
278 mInFlightWriteRequest
->MaybeResolveWithUndefined();
280 // Step 3. Set stream.[[inFlightWriteRequest]] to undefined.
281 mInFlightWriteRequest
= nullptr;
284 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write-with-error
285 void WritableStream::FinishInFlightWriteWithError(JSContext
* aCx
,
286 JS::Handle
<JS::Value
> aError
,
288 // Step 1. Assert: stream.[[inFlightWriteRequest]] is not undefined.
289 MOZ_ASSERT(mInFlightWriteRequest
);
291 // Step 2. Reject stream.[[inFlightWriteRequest]] with error.
292 mInFlightWriteRequest
->MaybeReject(aError
);
294 // Step 3. Set stream.[[inFlightWriteRequest]] to undefined.
295 mInFlightWriteRequest
= nullptr;
297 // Step 4. Assert: stream.[[state]] is "writable" or "erroring".
298 MOZ_ASSERT(mState
== WriterState::Writable
||
299 mState
== WriterState::Erroring
);
301 // Step 5. Perform ! WritableStreamDealWithRejection(stream, error).
302 DealWithRejection(aCx
, aError
, aRv
);
305 // https://streams.spec.whatwg.org/#writable-stream-mark-close-request-in-flight
306 void WritableStream::MarkCloseRequestInFlight() {
307 // Step 1. Assert: stream.[[inFlightCloseRequest]] is undefined.
308 MOZ_ASSERT(!mInFlightCloseRequest
);
310 // Step 2. Assert: stream.[[closeRequest]] is not undefined.
311 MOZ_ASSERT(mCloseRequest
);
313 // Step 3. Set stream.[[inFlightCloseRequest]] to stream.[[closeRequest]].
314 mInFlightCloseRequest
= mCloseRequest
;
316 // Step 4. Set stream.[[closeRequest]] to undefined.
317 mCloseRequest
= nullptr;
320 // https://streams.spec.whatwg.org/#writable-stream-mark-first-write-request-in-flight
321 void WritableStream::MarkFirstWriteRequestInFlight() {
322 // Step 1. Assert: stream.[[inFlightWriteRequest]] is undefined.
323 MOZ_ASSERT(!mInFlightWriteRequest
);
325 // Step 2. Assert: stream.[[writeRequests]] is not empty.
326 MOZ_ASSERT(!mWriteRequests
.IsEmpty());
328 // Step 3. Let writeRequest be stream.[[writeRequests]][0].
329 RefPtr
<Promise
> writeRequest
= mWriteRequests
.ElementAt(0);
331 // Step 4. Remove writeRequest from stream.[[writeRequests]].
332 mWriteRequests
.RemoveElementAt(0);
334 // Step 5. Set stream.[[inFlightWriteRequest]] to writeRequest.
335 mInFlightWriteRequest
= writeRequest
;
338 // https://streams.spec.whatwg.org/#writable-stream-reject-close-and-closed-promise-if-needed
339 void WritableStream::RejectCloseAndClosedPromiseIfNeeded() {
340 // Step 1. Assert: stream.[[state]] is "errored".
341 MOZ_ASSERT(mState
== WriterState::Errored
);
343 JS::Rooted
<JS::Value
> storedError(RootingCx(), mStoredError
);
344 // Step 2. If stream.[[closeRequest]] is not undefined,
346 // Step 2.1. Assert: stream.[[inFlightCloseRequest]] is undefined.
347 MOZ_ASSERT(!mInFlightCloseRequest
);
349 // Step 2.2. Reject stream.[[closeRequest]] with stream.[[storedError]].
350 mCloseRequest
->MaybeReject(storedError
);
352 // Step 2.3. Set stream.[[closeRequest]] to undefined.
353 mCloseRequest
= nullptr;
356 // Step 3. Let writer be stream.[[writer]].
357 RefPtr
<WritableStreamDefaultWriter
> writer
= mWriter
;
359 // Step 4. If writer is not undefined,
361 // Step 4.1. Reject writer.[[closedPromise]] with stream.[[storedError]].
362 RefPtr
<Promise
> closedPromise
= writer
->ClosedPromise();
363 closedPromise
->MaybeReject(storedError
);
365 // Step 4.2. Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
366 closedPromise
->SetSettledPromiseIsHandled();
370 // https://streams.spec.whatwg.org/#writable-stream-start-erroring
371 void WritableStream::StartErroring(JSContext
* aCx
,
372 JS::Handle
<JS::Value
> aReason
,
374 // Step 1. Assert: stream.[[storedError]] is undefined.
375 MOZ_ASSERT(mStoredError
.isUndefined());
377 // Step 2. Assert: stream.[[state]] is "writable".
378 MOZ_ASSERT(mState
== WriterState::Writable
);
380 // Step 3. Let controller be stream.[[controller]].
381 RefPtr
<WritableStreamDefaultController
> controller
= mController
;
382 // Step 4. Assert: controller is not undefined.
383 MOZ_ASSERT(controller
);
385 // Step 5. Set stream.[[state]] to "erroring".
386 mState
= WriterState::Erroring
;
388 // Step 6. Set stream.[[storedError]] to reason.
389 mStoredError
= aReason
;
391 // Step 7. Let writer be stream.[[writer]].
392 RefPtr
<WritableStreamDefaultWriter
> writer
= mWriter
;
393 // Step 8. If writer is not undefined, perform !
394 // WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason).
396 WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer
, aReason
);
399 // Step 9. If ! WritableStreamHasOperationMarkedInFlight(stream) is false
400 // and controller.[[started]] is true,
401 // perform !WritableStreamFinishErroring(stream).
402 if (!HasOperationMarkedInFlight() && controller
->Started()) {
403 FinishErroring(aCx
, aRv
);
407 // https://streams.spec.whatwg.org/#writable-stream-update-backpressure
408 void WritableStream::UpdateBackpressure(bool aBackpressure
) {
409 // Step 1. Assert: stream.[[state]] is "writable".
410 MOZ_ASSERT(mState
== WriterState::Writable
);
411 // Step 2. Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false.
412 MOZ_ASSERT(!CloseQueuedOrInFlight());
414 // Step 3. Let writer be stream.[[writer]].
415 RefPtr
<WritableStreamDefaultWriter
> writer
= mWriter
;
417 // Step 4. If writer is not undefined and backpressure is not
418 // stream.[[backpressure]],
419 if (writer
&& aBackpressure
!= mBackpressure
) {
420 // Step 4.1. If backpressure is true, set writer.[[readyPromise]] to a new
423 RefPtr
<Promise
> promise
=
424 Promise::CreateInfallible(writer
->GetParentObject());
425 writer
->SetReadyPromise(promise
);
427 // Step 4.2. Otherwise,
428 // Step 4.2.1. Assert: backpressure is false.
429 // Step 4.2.2. Resolve writer.[[readyPromise]] with undefined.
430 writer
->ReadyPromise()->MaybeResolveWithUndefined();
434 // Step 5. Set stream.[[backpressure]] to backpressure.
435 mBackpressure
= aBackpressure
;
438 // https://streams.spec.whatwg.org/#ws-constructor
439 already_AddRefed
<WritableStream
> WritableStream::Constructor(
440 const GlobalObject
& aGlobal
,
441 const Optional
<JS::Handle
<JSObject
*>>& aUnderlyingSink
,
442 const QueuingStrategy
& aStrategy
, ErrorResult
& aRv
) {
443 // Step 1. If underlyingSink is missing, set it to null.
444 JS::Rooted
<JSObject
*> underlyingSinkObj(
446 aUnderlyingSink
.WasPassed() ? aUnderlyingSink
.Value() : nullptr);
448 // Step 2. Let underlyingSinkDict be underlyingSink, converted to
449 // an IDL value of type UnderlyingSink.
450 RootedDictionary
<UnderlyingSink
> underlyingSinkDict(aGlobal
.Context());
451 if (underlyingSinkObj
) {
452 JS::Rooted
<JS::Value
> objValue(aGlobal
.Context(),
453 JS::ObjectValue(*underlyingSinkObj
));
454 dom::BindingCallContext
callCx(aGlobal
.Context(),
455 "WritableStream.constructor");
456 aRv
.MightThrowJSException();
457 if (!underlyingSinkDict
.Init(callCx
, objValue
)) {
458 aRv
.StealExceptionFromJSContext(aGlobal
.Context());
463 // Step 3. If underlyingSinkDict["type"] exists, throw a RangeError exception.
464 if (!underlyingSinkDict
.mType
.isUndefined()) {
465 aRv
.ThrowRangeError("Implementation preserved member 'type'");
469 // Step 4. Perform ! InitializeWritableStream(this).
470 RefPtr
<WritableStream
> writableStream
=
471 new WritableStream(aGlobal
, HoldDropJSObjectsCaller::Implicit
);
473 // Step 5. Let sizeAlgorithm be ! ExtractSizeAlgorithm(strategy).
475 // Implementation Note: The specification demands that if the size doesn't
476 // exist, we instead would provide an algorithm that returns 1. Instead, we
477 // will teach callers that a missing callback should simply return 1, rather
478 // than gin up a fake callback here.
480 // This decision may need to be revisited if the default action ever diverges
481 // within the specification.
482 RefPtr
<QueuingStrategySize
> sizeAlgorithm
=
483 aStrategy
.mSize
.WasPassed() ? &aStrategy
.mSize
.Value() : nullptr;
485 // Step 6. Let highWaterMark be ? ExtractHighWaterMark(strategy, 1).
486 double highWaterMark
= ExtractHighWaterMark(aStrategy
, 1, aRv
);
491 // Step 7. Perform ? SetUpWritableStreamDefaultControllerFromUnderlyingSink(
492 // this, underlyingSink, underlyingSinkDict, highWaterMark, sizeAlgorithm).
493 SetUpWritableStreamDefaultControllerFromUnderlyingSink(
494 aGlobal
.Context(), writableStream
, underlyingSinkObj
, underlyingSinkDict
,
495 highWaterMark
, sizeAlgorithm
, aRv
);
500 return writableStream
.forget();
503 namespace streams_abstract
{
504 // https://streams.spec.whatwg.org/#writable-stream-abort
505 already_AddRefed
<Promise
> WritableStreamAbort(JSContext
* aCx
,
506 WritableStream
* aStream
,
507 JS::Handle
<JS::Value
> aReason
,
509 // Step 1. If stream.[[state]] is "closed" or "errored", return a promise
510 // resolved with undefined.
511 if (aStream
->State() == WritableStream::WriterState::Closed
||
512 aStream
->State() == WritableStream::WriterState::Errored
) {
513 RefPtr
<Promise
> promise
=
514 Promise::CreateInfallible(aStream
->GetParentObject());
515 promise
->MaybeResolveWithUndefined();
516 return promise
.forget();
519 // Step 2. Signal abort on stream.[[controller]].[[signal]] with reason.
520 RefPtr
<WritableStreamDefaultController
> controller
= aStream
->Controller();
521 controller
->Signal()->SignalAbort(aReason
);
523 // Step 3. Let state be stream.[[state]].
524 WritableStream::WriterState state
= aStream
->State();
526 // Step 4. If state is "closed" or "errored", return a promise resolved with
527 // undefined. Note: We re-check the state because signaling abort runs author
528 // code and that might have changed the state.
529 if (aStream
->State() == WritableStream::WriterState::Closed
||
530 aStream
->State() == WritableStream::WriterState::Errored
) {
531 RefPtr
<Promise
> promise
=
532 Promise::CreateInfallible(aStream
->GetParentObject());
533 promise
->MaybeResolveWithUndefined();
534 return promise
.forget();
537 // Step 5. If stream.[[pendingAbortRequest]] is not undefined, return
538 // stream.[[pendingAbortRequest]]'s promise.
539 if (aStream
->GetPendingAbortRequestPromise()) {
540 RefPtr
<Promise
> promise
= aStream
->GetPendingAbortRequestPromise();
541 return promise
.forget();
544 // Step 6. Assert: state is "writable" or "erroring".
545 MOZ_ASSERT(state
== WritableStream::WriterState::Writable
||
546 state
== WritableStream::WriterState::Erroring
);
548 // Step 7. Let wasAlreadyErroring be false.
549 bool wasAlreadyErroring
= false;
551 // Step 8. If state is "erroring",
552 JS::Rooted
<JS::Value
> reason(aCx
, aReason
);
553 if (state
== WritableStream::WriterState::Erroring
) {
554 // Step 8.1. Set wasAlreadyErroring to true.
555 wasAlreadyErroring
= true;
556 // Step 8.2. Set reason to undefined.
557 reason
.setUndefined();
560 // Step 9. Let promise be a new promise.
561 RefPtr
<Promise
> promise
=
562 Promise::CreateInfallible(aStream
->GetParentObject());
564 // Step 10. Set stream.[[pendingAbortRequest]] to a new pending abort request
565 // whose promise is promise, reason is reason, and was already erroring is
566 // wasAlreadyErroring.
567 aStream
->SetPendingAbortRequest(promise
, reason
, wasAlreadyErroring
);
569 // Step 11. If wasAlreadyErroring is false, perform !
570 // WritableStreamStartErroring(stream, reason).
571 if (!wasAlreadyErroring
) {
572 aStream
->StartErroring(aCx
, reason
, aRv
);
578 // Step 12. Return promise.
579 return promise
.forget();
581 } // namespace streams_abstract
583 // https://streams.spec.whatwg.org/#ws-abort
584 already_AddRefed
<Promise
> WritableStream::Abort(JSContext
* aCx
,
585 JS::Handle
<JS::Value
> aReason
,
587 // Step 1. If ! IsWritableStreamLocked(this) is true, return a promise
588 // rejected with a TypeError exception.
590 return Promise::CreateRejectedWithTypeError(
591 GetParentObject(), "Canceled Locked Stream"_ns
, aRv
);
594 // Step 2. Return ! WritableStreamAbort(this, reason).
595 RefPtr
<WritableStream
> thisRefPtr
= this;
596 return WritableStreamAbort(aCx
, thisRefPtr
, aReason
, aRv
);
599 namespace streams_abstract
{
600 // https://streams.spec.whatwg.org/#writable-stream-close
601 already_AddRefed
<Promise
> WritableStreamClose(JSContext
* aCx
,
602 WritableStream
* aStream
,
604 // Step 1. Let state be stream.[[state]].
605 WritableStream::WriterState state
= aStream
->State();
607 // Step 2. If state is "closed" or "errored", return a promise rejected with a
608 // TypeError exception.
609 if (state
== WritableStream::WriterState::Closed
||
610 state
== WritableStream::WriterState::Errored
) {
611 return Promise::CreateRejectedWithTypeError(
612 aStream
->GetParentObject(),
613 "Can not close stream after closing or error"_ns
, aRv
);
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
=
625 Promise::CreateInfallible(aStream
->GetParentObject());
627 // Step 6. Set stream.[[closeRequest]] to promise.
628 aStream
->SetCloseRequest(promise
);
630 // Step 7. Let writer be stream.[[writer]].
631 RefPtr
<WritableStreamDefaultWriter
> writer
= aStream
->GetWriter();
633 // Step 8. If writer is not undefined, and stream.[[backpressure]] is true,
634 // and state is "writable", resolve writer.[[readyPromise]] with undefined.
635 if (writer
&& aStream
->Backpressure() &&
636 state
== WritableStream::WriterState::Writable
) {
637 writer
->ReadyPromise()->MaybeResolveWithUndefined();
641 // Perform ! WritableStreamDefaultControllerClose(stream.[[controller]]).
642 RefPtr
<WritableStreamDefaultController
> controller
= aStream
->Controller();
643 WritableStreamDefaultControllerClose(aCx
, controller
, aRv
);
648 // Step 10. Return promise.
649 return promise
.forget();
651 } // namespace streams_abstract
653 // https://streams.spec.whatwg.org/#ws-close
654 already_AddRefed
<Promise
> WritableStream::Close(JSContext
* aCx
,
656 // Step 1. If ! IsWritableStreamLocked(this) is true, return a promise
657 // rejected with a TypeError exception.
659 return Promise::CreateRejectedWithTypeError(
660 GetParentObject(), "Can not close locked stream"_ns
, aRv
);
663 // Step 2. If ! WritableStreamCloseQueuedOrInFlight(this) is true, return a
664 // promise rejected with a TypeError exception.
665 if (CloseQueuedOrInFlight()) {
666 return Promise::CreateRejectedWithTypeError(
667 GetParentObject(), "Stream is already closing"_ns
, aRv
);
670 // Step 3. Return ! WritableStreamClose(this).
671 RefPtr
<WritableStream
> thisRefPtr
= this;
672 return WritableStreamClose(aCx
, thisRefPtr
, aRv
);
675 namespace streams_abstract
{
676 // https://streams.spec.whatwg.org/#acquire-writable-stream-default-writer
677 already_AddRefed
<WritableStreamDefaultWriter
>
678 AcquireWritableStreamDefaultWriter(WritableStream
* aStream
, ErrorResult
& aRv
) {
679 // Step 1. Let writer be a new WritableStreamDefaultWriter.
680 RefPtr
<WritableStreamDefaultWriter
> writer
=
681 new WritableStreamDefaultWriter(aStream
->GetParentObject());
683 // Step 2. Perform ? SetUpWritableStreamDefaultWriter(writer, stream).
684 SetUpWritableStreamDefaultWriter(writer
, aStream
, aRv
);
689 // Step 3. Return writer.
690 return writer
.forget();
692 } // namespace streams_abstract
694 // https://streams.spec.whatwg.org/#create-writable-stream
695 already_AddRefed
<WritableStream
> WritableStream::CreateAbstract(
696 JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
697 UnderlyingSinkAlgorithmsBase
* aAlgorithms
, double aHighWaterMark
,
698 QueuingStrategySize
* aSizeAlgorithm
, ErrorResult
& aRv
) {
699 // Step 1: Assert: ! IsNonNegativeNumber(highWaterMark) is true.
700 MOZ_ASSERT(IsNonNegativeNumber(aHighWaterMark
));
702 // Step 2: Let stream be a new WritableStream.
703 // Step 3: Perform ! InitializeWritableStream(stream).
704 RefPtr
<WritableStream
> stream
= new WritableStream(
705 aGlobal
, WritableStream::HoldDropJSObjectsCaller::Implicit
);
707 // Step 4: Let controller be a new WritableStreamDefaultController.
709 MakeRefPtr
<WritableStreamDefaultController
>(aGlobal
, *stream
);
711 // Step 5: Perform ? SetUpWritableStreamDefaultController(stream, controller,
712 // startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm,
713 // highWaterMark, sizeAlgorithm).
714 SetUpWritableStreamDefaultController(aCx
, stream
, controller
, aAlgorithms
,
715 aHighWaterMark
, aSizeAlgorithm
, aRv
);
720 // Step 6: Return stream.
721 return stream
.forget();
724 already_AddRefed
<WritableStreamDefaultWriter
> WritableStream::GetWriter(
726 return AcquireWritableStreamDefaultWriter(this, aRv
);
729 namespace streams_abstract
{
730 // https://streams.spec.whatwg.org/#writable-stream-add-write-request
731 already_AddRefed
<Promise
> WritableStreamAddWriteRequest(
732 WritableStream
* aStream
) {
733 // Step 1. Assert: ! IsWritableStreamLocked(stream) is true.
734 MOZ_ASSERT(IsWritableStreamLocked(aStream
));
736 // Step 2. Assert: stream.[[state]] is "writable".
737 MOZ_ASSERT(aStream
->State() == WritableStream::WriterState::Writable
);
739 // Step 3. Let promise be a new promise.
740 RefPtr
<Promise
> promise
=
741 Promise::CreateInfallible(aStream
->GetParentObject());
743 // Step 4. Append promise to stream.[[writeRequests]].
744 aStream
->AppendWriteRequest(promise
);
746 // Step 5. Return promise.
747 return promise
.forget();
749 } // namespace streams_abstract
751 // https://streams.spec.whatwg.org/#writablestream-set-up
752 // _BOUNDARY because `aAlgorithms->StartCallback` (called by
753 // SetUpWritableStreamDefaultController below) should not be able to run script
755 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void WritableStream::SetUpNative(
756 JSContext
* aCx
, UnderlyingSinkAlgorithmsWrapper
& aAlgorithms
,
757 Maybe
<double> aHighWaterMark
, QueuingStrategySize
* aSizeAlgorithm
,
759 // an optional number highWaterMark (default 1)
760 double highWaterMark
= aHighWaterMark
.valueOr(1);
761 // and if given, highWaterMark must be a non-negative, non-NaN number.
762 MOZ_ASSERT(IsNonNegativeNumber(highWaterMark
));
764 // Step 1: Let startAlgorithm be an algorithm that returns undefined.
765 // Step 2: Let closeAlgorithmWrapper be an algorithm that runs these steps:
766 // Step 3: Let abortAlgorithmWrapper be an algorithm that runs these steps:
767 // (Covered by UnderlyingSinkAlgorithmsWrapper)
769 // Step 4: If sizeAlgorithm was not given, then set it to an algorithm that
770 // returns 1. (Callers will treat nullptr as such, see
771 // WritableStream::Constructor for details)
773 // Step 5: Perform ! InitializeWritableStream(stream).
774 // (Covered by the constructor)
776 // Step 6: Let controller be a new WritableStreamDefaultController.
778 MakeRefPtr
<WritableStreamDefaultController
>(GetParentObject(), *this);
780 // Step 7: Perform ! SetUpWritableStreamDefaultController(stream, controller,
781 // startAlgorithm, writeAlgorithm, closeAlgorithmWrapper,
782 // abortAlgorithmWrapper, highWaterMark, sizeAlgorithm).
783 SetUpWritableStreamDefaultController(aCx
, this, controller
, &aAlgorithms
,
784 highWaterMark
, aSizeAlgorithm
, aRv
);
787 already_AddRefed
<WritableStream
> WritableStream::CreateNative(
788 JSContext
* aCx
, nsIGlobalObject
& aGlobal
,
789 UnderlyingSinkAlgorithmsWrapper
& aAlgorithms
, Maybe
<double> aHighWaterMark
,
790 QueuingStrategySize
* aSizeAlgorithm
, ErrorResult
& aRv
) {
791 RefPtr
<WritableStream
> stream
= new WritableStream(
792 &aGlobal
, WritableStream::HoldDropJSObjectsCaller::Implicit
);
793 stream
->SetUpNative(aCx
, aAlgorithms
, aHighWaterMark
, aSizeAlgorithm
, aRv
);
797 return stream
.forget();
800 // https://streams.spec.whatwg.org/#writablestream-error
801 // To error a WritableStream stream given a JavaScript value e, perform !
802 // WritableStreamDefaultControllerErrorIfNeeded(stream.[[controller]], e).
803 void WritableStream::ErrorNative(JSContext
* aCx
, JS::Handle
<JS::Value
> aError
,
805 // MOZ_KnownLive here instead of MOZ_KNOWN_LIVE at the field, because
806 // mController is set outside of the constructor
807 WritableStreamDefaultControllerErrorIfNeeded(aCx
, MOZ_KnownLive(mController
),
811 } // namespace mozilla::dom