Bug 1857841 - pt 3. Add a new page kind named "fresh" r=glandium
[gecko.git] / dom / streams / WritableStream.cpp
blob8dab9a564e1fd80101a74b51f7b930664956e704
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"
10 #include "js/Array.h"
11 #include "js/PropertyAndElement.h"
12 #include "js/TypeDecls.h"
13 #include "js/Value.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"
29 #include "nsCOMPtr.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(
40 WritableStream,
41 (mGlobal, mCloseRequest, mController, mInFlightWriteRequest,
42 mInFlightCloseRequest, mPendingAbortRequestPromise, mWriter,
43 mWriteRequests),
44 (mPendingAbortRequestReason, mStoredError))
46 NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStream)
47 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(WritableStream,
48 LastRelease())
49 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableStream)
50 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
51 NS_INTERFACE_MAP_ENTRY(nsISupports)
52 NS_INTERFACE_MAP_END
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,
85 ErrorResult& aRv) {
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);
92 // Step 2.2. Return.
93 return;
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
109 // false.
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();
136 // Step 8.2. Return.
137 return;
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.
158 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);
165 if (aRv.Failed()) {
166 return;
169 // Step 13 + 14.
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
218 // undefined.
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
231 // undefined.
232 if (mWriter) {
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,
245 ErrorResult& aRv) {
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,
287 ErrorResult& aRv) {
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,
345 if (mCloseRequest) {
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,
360 if (writer) {
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,
373 ErrorResult& aRv) {
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).
395 if (writer) {
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
421 // promise.
422 if (aBackpressure) {
423 RefPtr<Promise> promise =
424 Promise::CreateInfallible(writer->GetParentObject());
425 writer->SetReadyPromise(promise);
426 } else {
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(
445 aGlobal.Context(),
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());
459 return nullptr;
463 // Step 3. If underlyingSinkDict["type"] exists, throw a RangeError exception.
464 if (!underlyingSinkDict.mType.isUndefined()) {
465 aRv.ThrowRangeError("Implementation preserved member 'type'");
466 return nullptr;
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);
487 if (aRv.Failed()) {
488 return nullptr;
491 // Step 7. Perform ? SetUpWritableStreamDefaultControllerFromUnderlyingSink(
492 // this, underlyingSink, underlyingSinkDict, highWaterMark, sizeAlgorithm).
493 SetUpWritableStreamDefaultControllerFromUnderlyingSink(
494 aGlobal.Context(), writableStream, underlyingSinkObj, underlyingSinkDict,
495 highWaterMark, sizeAlgorithm, aRv);
496 if (aRv.Failed()) {
497 return nullptr;
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,
508 ErrorResult& aRv) {
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);
573 if (aRv.Failed()) {
574 return nullptr;
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,
586 ErrorResult& aRv) {
587 // Step 1. If ! IsWritableStreamLocked(this) is true, return a promise
588 // rejected with a TypeError exception.
589 if (Locked()) {
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,
603 ErrorResult& aRv) {
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();
640 // Step 9.
641 // Perform ! WritableStreamDefaultControllerClose(stream.[[controller]]).
642 RefPtr<WritableStreamDefaultController> controller = aStream->Controller();
643 WritableStreamDefaultControllerClose(aCx, controller, aRv);
644 if (aRv.Failed()) {
645 return nullptr;
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,
655 ErrorResult& aRv) {
656 // Step 1. If ! IsWritableStreamLocked(this) is true, return a promise
657 // rejected with a TypeError exception.
658 if (Locked()) {
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);
685 if (aRv.Failed()) {
686 return nullptr;
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.
708 auto controller =
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);
716 if (aRv.Failed()) {
717 return nullptr;
720 // Step 6: Return stream.
721 return stream.forget();
724 already_AddRefed<WritableStreamDefaultWriter> WritableStream::GetWriter(
725 ErrorResult& aRv) {
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
754 // in this case.
755 MOZ_CAN_RUN_SCRIPT_BOUNDARY void WritableStream::SetUpNative(
756 JSContext* aCx, UnderlyingSinkAlgorithmsWrapper& aAlgorithms,
757 Maybe<double> aHighWaterMark, QueuingStrategySize* aSizeAlgorithm,
758 ErrorResult& aRv) {
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.
777 auto controller =
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);
794 if (aRv.Failed()) {
795 return nullptr;
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,
804 ErrorResult& aRv) {
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),
808 aError, aRv);
811 } // namespace mozilla::dom