Bug 1857841 - pt 3. Add a new page kind named "fresh" r=glandium
[gecko.git] / dom / streams / WritableStreamDefaultWriter.cpp
blob589eada7e2d1273af81fad1cb6c13c20358053c7
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/WritableStreamDefaultWriter.h"
8 #include "js/Array.h"
9 #include "js/TypeDecls.h"
10 #include "js/Value.h"
11 #include "mozilla/AlreadyAddRefed.h"
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Attributes.h"
14 #include "mozilla/CycleCollectedJSContext.h"
15 #include "mozilla/HoldDropJSObjects.h"
16 #include "mozilla/dom/WritableStream.h"
17 #include "mozilla/dom/WritableStreamDefaultWriterBinding.h"
18 #include "nsCOMPtr.h"
20 #include "mozilla/dom/Promise-inl.h"
21 #include "nsIGlobalObject.h"
22 #include "nsISupports.h"
24 namespace mozilla::dom {
26 using namespace streams_abstract;
28 NS_IMPL_CYCLE_COLLECTION_CLASS(WritableStreamDefaultWriter)
29 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WritableStreamDefaultWriter)
30 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mStream, mReadyPromise,
31 mClosedPromise)
32 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
33 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WritableStreamDefaultWriter)
36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mStream, mReadyPromise,
37 mClosedPromise)
38 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
40 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WritableStreamDefaultWriter)
41 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
42 NS_IMPL_CYCLE_COLLECTION_TRACE_END
44 NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStreamDefaultWriter)
45 NS_IMPL_CYCLE_COLLECTING_RELEASE(WritableStreamDefaultWriter)
46 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableStreamDefaultWriter)
47 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
48 NS_INTERFACE_MAP_ENTRY(nsISupports)
49 NS_INTERFACE_MAP_END
51 WritableStreamDefaultWriter::WritableStreamDefaultWriter(
52 nsIGlobalObject* aGlobal)
53 : mGlobal(aGlobal) {
54 mozilla::HoldJSObjects(this);
57 WritableStreamDefaultWriter::~WritableStreamDefaultWriter() {
58 mozilla::DropJSObjects(this);
61 void WritableStreamDefaultWriter::SetReadyPromise(Promise* aPromise) {
62 MOZ_ASSERT(aPromise);
63 mReadyPromise = aPromise;
66 void WritableStreamDefaultWriter::SetClosedPromise(Promise* aPromise) {
67 MOZ_ASSERT(aPromise);
68 mClosedPromise = aPromise;
71 JSObject* WritableStreamDefaultWriter::WrapObject(
72 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
73 return WritableStreamDefaultWriter_Binding::Wrap(aCx, this, aGivenProto);
76 /* static */
77 already_AddRefed<WritableStreamDefaultWriter>
78 WritableStreamDefaultWriter::Constructor(const GlobalObject& aGlobal,
79 WritableStream& aStream,
80 ErrorResult& aRv) {
81 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
82 RefPtr<WritableStreamDefaultWriter> writer =
83 new WritableStreamDefaultWriter(global);
84 SetUpWritableStreamDefaultWriter(writer, &aStream, aRv);
85 if (aRv.Failed()) {
86 return nullptr;
88 return writer.forget();
91 already_AddRefed<Promise> WritableStreamDefaultWriter::Closed() {
92 RefPtr<Promise> closedPromise = mClosedPromise;
93 return closedPromise.forget();
96 already_AddRefed<Promise> WritableStreamDefaultWriter::Ready() {
97 RefPtr<Promise> readyPromise = mReadyPromise;
98 return readyPromise.forget();
101 namespace streams_abstract {
102 // https://streams.spec.whatwg.org/#writable-stream-default-writer-get-desired-size
103 Nullable<double> WritableStreamDefaultWriterGetDesiredSize(
104 WritableStreamDefaultWriter* aWriter) {
105 // Step 1. Let stream be writer.[[stream]].
106 RefPtr<WritableStream> stream = aWriter->GetStream();
108 // Step 2. Let state be stream.[[state]].
109 WritableStream::WriterState state = stream->State();
111 // Step 3. If state is "errored" or "erroring", return null.
112 if (state == WritableStream::WriterState::Errored ||
113 state == WritableStream::WriterState::Erroring) {
114 return nullptr;
117 // Step 4. If state is "closed", return 0.
118 if (state == WritableStream::WriterState::Closed) {
119 return 0.0;
122 // Step 5. Return
123 // ! WritableStreamDefaultControllerGetDesiredSize(stream.[[controller]]).
124 return stream->Controller()->GetDesiredSize();
126 } // namespace streams_abstract
128 // https://streams.spec.whatwg.org/#default-writer-desired-size
129 Nullable<double> WritableStreamDefaultWriter::GetDesiredSize(ErrorResult& aRv) {
130 // Step 1. If this.[[stream]] is undefined, throw a TypeError exception.
131 if (!mStream) {
132 aRv.ThrowTypeError("Missing stream");
133 return nullptr;
136 // Step 2. Return ! WritableStreamDefaultWriterGetDesiredSize(this).
137 RefPtr<WritableStreamDefaultWriter> thisRefPtr = this;
138 return WritableStreamDefaultWriterGetDesiredSize(thisRefPtr);
141 // https://streams.spec.whatwg.org/#writable-stream-default-writer-abort
142 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> WritableStreamDefaultWriterAbort(
143 JSContext* aCx, WritableStreamDefaultWriter* aWriter,
144 JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
145 // Step 1. Let stream be writer.[[stream]].
146 RefPtr<WritableStream> stream = aWriter->GetStream();
148 // Step 2. Assert: stream is not undefined.
149 MOZ_ASSERT(stream);
151 // Step 3. Return ! WritableStreamAbort(stream, reason).
152 return WritableStreamAbort(aCx, stream, aReason, aRv);
155 // https://streams.spec.whatwg.org/#default-writer-abort
156 already_AddRefed<Promise> WritableStreamDefaultWriter::Abort(
157 JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
158 // Step 1. If this.[[stream]] is undefined, return a promise rejected with a
159 // TypeError exception.
160 if (!mStream) {
161 aRv.ThrowTypeError("Missing stream");
162 return nullptr;
165 // Step 2. Return ! WritableStreamDefaultWriterAbort(this, reason).
166 RefPtr<WritableStreamDefaultWriter> thisRefPtr = this;
167 return WritableStreamDefaultWriterAbort(aCx, thisRefPtr, aReason, aRv);
170 // https://streams.spec.whatwg.org/#writable-stream-default-writer-close
171 MOZ_CAN_RUN_SCRIPT static already_AddRefed<Promise>
172 WritableStreamDefaultWriterClose(JSContext* aCx,
173 WritableStreamDefaultWriter* aWriter,
174 ErrorResult& aRv) {
175 // Step 1. Let stream be writer.[[stream]].
176 RefPtr<WritableStream> stream = aWriter->GetStream();
178 // Step 2. Assert: stream is not undefined.
179 MOZ_ASSERT(stream);
181 // Step 3. Return ! WritableStreamClose(stream).
182 return WritableStreamClose(aCx, stream, aRv);
185 // https://streams.spec.whatwg.org/#default-writer-close
186 already_AddRefed<Promise> WritableStreamDefaultWriter::Close(JSContext* aCx,
187 ErrorResult& aRv) {
188 // Step 1. Let stream be this.[[stream]].
189 RefPtr<WritableStream> stream = mStream;
191 // Step 2. If stream is undefined, return a promise rejected with a TypeError
192 // exception.
193 if (!stream) {
194 aRv.ThrowTypeError("Missing stream");
195 return nullptr;
198 // Step 3. If ! WritableStreamCloseQueuedOrInFlight(stream) is true,
199 // return a promise rejected with a TypeError exception.
200 if (stream->CloseQueuedOrInFlight()) {
201 aRv.ThrowTypeError("Stream is closing");
202 return nullptr;
205 // Step 3. Return ! WritableStreamDefaultWriterClose(this).
206 RefPtr<WritableStreamDefaultWriter> thisRefPtr = this;
207 return WritableStreamDefaultWriterClose(aCx, thisRefPtr, aRv);
210 namespace streams_abstract {
211 // https://streams.spec.whatwg.org/#writable-stream-default-writer-release
212 void WritableStreamDefaultWriterRelease(JSContext* aCx,
213 WritableStreamDefaultWriter* aWriter) {
214 // Step 1. Let stream be writer.[[stream]].
215 RefPtr<WritableStream> stream = aWriter->GetStream();
217 // Step 2. Assert: stream is not undefined.
218 MOZ_ASSERT(stream);
220 // Step 3. Assert: stream.[[writer]] is writer.
221 MOZ_ASSERT(stream->GetWriter() == aWriter);
223 // Step 4. Let releasedError be a new TypeError.
224 JS::Rooted<JS::Value> releasedError(RootingCx(), JS::UndefinedValue());
226 ErrorResult rv;
227 rv.ThrowTypeError("Releasing lock");
228 bool ok = ToJSValue(aCx, std::move(rv), &releasedError);
229 MOZ_RELEASE_ASSERT(ok, "must be ok");
232 // Step 5. Perform !
233 // WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer,
234 // releasedError).
235 WritableStreamDefaultWriterEnsureReadyPromiseRejected(aWriter, releasedError);
237 // Step 6. Perform !
238 // WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer,
239 // releasedError).
240 WritableStreamDefaultWriterEnsureClosedPromiseRejected(aWriter,
241 releasedError);
243 // Step 7. Set stream.[[writer]] to undefined.
244 stream->SetWriter(nullptr);
246 // Step 8. Set writer.[[stream]] to undefined.
247 aWriter->SetStream(nullptr);
249 } // namespace streams_abstract
251 // https://streams.spec.whatwg.org/#default-writer-release-lock
252 void WritableStreamDefaultWriter::ReleaseLock(JSContext* aCx) {
253 // Step 1. Let stream be this.[[stream]].
254 RefPtr<WritableStream> stream = mStream;
256 // Step 2. If stream is undefined, return.
257 if (!stream) {
258 return;
261 // Step 3. Assert: stream.[[writer]] is not undefined.
262 MOZ_ASSERT(stream->GetWriter());
264 // Step 4. Perform ! WritableStreamDefaultWriterRelease(this).
265 RefPtr<WritableStreamDefaultWriter> thisRefPtr = this;
266 return WritableStreamDefaultWriterRelease(aCx, thisRefPtr);
269 namespace streams_abstract {
270 // https://streams.spec.whatwg.org/#writable-stream-default-writer-write
271 already_AddRefed<Promise> WritableStreamDefaultWriterWrite(
272 JSContext* aCx, WritableStreamDefaultWriter* aWriter,
273 JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
274 // Step 1. Let stream be writer.[[stream]].
275 RefPtr<WritableStream> stream = aWriter->GetStream();
277 // Step 2. Assert: stream is not undefined.
278 MOZ_ASSERT(stream);
280 // Step 3. Let controller be stream.[[controller]].
281 RefPtr<WritableStreamDefaultController> controller = stream->Controller();
283 // Step 4. Let chunkSize be !
284 // WritableStreamDefaultControllerGetChunkSize(controller, chunk).
285 double chunkSize =
286 WritableStreamDefaultControllerGetChunkSize(aCx, controller, aChunk, aRv);
287 if (aRv.Failed()) {
288 return nullptr;
291 // Step 5. If stream is not equal to writer.[[stream]], return a promise
292 // rejected with a TypeError exception.
293 if (stream != aWriter->GetStream()) {
294 aRv.ThrowTypeError(
295 "Can not write on WritableStream owned by another writer.");
296 return nullptr;
299 // Step 6. Let state be stream.[[state]].
300 WritableStream::WriterState state = stream->State();
302 // Step 7. If state is "errored", return a promise rejected with
303 // stream.[[storedError]].
304 if (state == WritableStream::WriterState::Errored) {
305 JS::Rooted<JS::Value> error(aCx, stream->StoredError());
306 return Promise::CreateRejected(aWriter->GetParentObject(), error, aRv);
309 // Step 8. If ! WritableStreamCloseQueuedOrInFlight(stream) is true or state
310 // is "closed", return a promise rejected with a TypeError exception
311 // indicating that the stream is closing or closed.
312 if (stream->CloseQueuedOrInFlight() ||
313 state == WritableStream::WriterState::Closed) {
314 return Promise::CreateRejectedWithTypeError(
315 aWriter->GetParentObject(), "Stream is closed or closing"_ns, aRv);
318 // Step 9. If state is "erroring", return a promise rejected with
319 // stream.[[storedError]].
320 if (state == WritableStream::WriterState::Erroring) {
321 JS::Rooted<JS::Value> error(aCx, stream->StoredError());
322 return Promise::CreateRejected(aWriter->GetParentObject(), error, aRv);
325 // Step 10. Assert: state is "writable".
326 MOZ_ASSERT(state == WritableStream::WriterState::Writable);
328 // Step 11. Let promise be ! WritableStreamAddWriteRequest(stream).
329 RefPtr<Promise> promise = WritableStreamAddWriteRequest(stream);
331 // Step 12. Perform ! WritableStreamDefaultControllerWrite(controller, chunk,
332 // chunkSize).
333 WritableStreamDefaultControllerWrite(aCx, controller, aChunk, chunkSize, aRv);
334 if (aRv.Failed()) {
335 return nullptr;
338 // Step 13. Return promise.
339 return promise.forget();
341 } // namespace streams_abstract
343 // https://streams.spec.whatwg.org/#default-writer-write
344 already_AddRefed<Promise> WritableStreamDefaultWriter::Write(
345 JSContext* aCx, JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
346 // Step 1. If this.[[stream]] is undefined, return a promise rejected with a
347 // TypeError exception.
348 if (!mStream) {
349 aRv.ThrowTypeError("Missing stream");
350 return nullptr;
353 // Step 2. Return ! WritableStreamDefaultWriterWrite(this, chunk).
354 return WritableStreamDefaultWriterWrite(aCx, this, aChunk, aRv);
357 namespace streams_abstract {
359 // https://streams.spec.whatwg.org/#set-up-writable-stream-default-writer
360 void SetUpWritableStreamDefaultWriter(WritableStreamDefaultWriter* aWriter,
361 WritableStream* aStream,
362 ErrorResult& aRv) {
363 // Step 1. If ! IsWritableStreamLocked(stream) is true, throw a TypeError
364 // exception.
365 if (IsWritableStreamLocked(aStream)) {
366 aRv.ThrowTypeError("WritableStream is already locked!");
367 return;
370 // Step 2. Set writer.[[stream]] to stream.
371 aWriter->SetStream(aStream);
373 // Step 3. Set stream.[[writer]] to writer.
374 aStream->SetWriter(aWriter);
376 // Step 4. Let state be stream.[[state]].
377 WritableStream::WriterState state = aStream->State();
379 // Step 5. If state is "writable",
380 if (state == WritableStream::WriterState::Writable) {
381 RefPtr<Promise> readyPromise =
382 Promise::CreateInfallible(aWriter->GetParentObject());
384 // Step 5.1 If ! WritableStreamCloseQueuedOrInFlight(stream) is false and
385 // stream.[[backpressure]] is true, set writer.[[readyPromise]] to a new
386 // promise.
387 if (!aStream->CloseQueuedOrInFlight() && aStream->Backpressure()) {
388 aWriter->SetReadyPromise(readyPromise);
389 } else {
390 // Step 5.2. Otherwise, set writer.[[readyPromise]] to a promise resolved
391 // with undefined.
392 readyPromise->MaybeResolveWithUndefined();
393 aWriter->SetReadyPromise(readyPromise);
396 // Step 5.3. Set writer.[[closedPromise]] to a new promise.
397 RefPtr<Promise> closedPromise =
398 Promise::CreateInfallible(aWriter->GetParentObject());
399 aWriter->SetClosedPromise(closedPromise);
400 } else if (state == WritableStream::WriterState::Erroring) {
401 // Step 6. Otherwise, if state is "erroring",
403 // Step 6.1. Set writer.[[readyPromise]] to a promise rejected with
404 // stream.[[storedError]].
405 JS::Rooted<JS::Value> storedError(RootingCx(), aStream->StoredError());
406 RefPtr<Promise> readyPromise =
407 Promise::CreateInfallible(aWriter->GetParentObject());
408 readyPromise->MaybeReject(storedError);
409 aWriter->SetReadyPromise(readyPromise);
411 // Step 6.2. Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
412 readyPromise->SetSettledPromiseIsHandled();
414 // Step 6.3. Set writer.[[closedPromise]] to a new promise.
415 RefPtr<Promise> closedPromise =
416 Promise::CreateInfallible(aWriter->GetParentObject());
417 aWriter->SetClosedPromise(closedPromise);
418 } else if (state == WritableStream::WriterState::Closed) {
419 // Step 7. Otherwise, if state is "closed",
420 // Step 7.1. Set writer.[[readyPromise]] to a promise resolved with
421 // undefined.
422 RefPtr<Promise> readyPromise =
423 Promise::CreateResolvedWithUndefined(aWriter->GetParentObject(), aRv);
424 if (aRv.Failed()) {
425 return;
427 aWriter->SetReadyPromise(readyPromise);
429 // Step 7.2. Set writer.[[closedPromise]] to a promise resolved with
430 // undefined.
431 RefPtr<Promise> closedPromise =
432 Promise::CreateResolvedWithUndefined(aWriter->GetParentObject(), aRv);
433 if (aRv.Failed()) {
434 return;
436 aWriter->SetClosedPromise(closedPromise);
437 } else {
438 // Step 8. Otherwise,
439 // Step 8.1 Assert: state is "errored".
440 MOZ_ASSERT(state == WritableStream::WriterState::Errored);
442 // Step 8.2. Step Let storedError be stream.[[storedError]].
443 JS::Rooted<JS::Value> storedError(RootingCx(), aStream->StoredError());
445 // Step 8.3. Set writer.[[readyPromise]] to a promise rejected with
446 // storedError.
447 RefPtr<Promise> readyPromise =
448 Promise::CreateInfallible(aWriter->GetParentObject());
449 readyPromise->MaybeReject(storedError);
450 aWriter->SetReadyPromise(readyPromise);
452 // Step 8.4. Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
453 readyPromise->SetSettledPromiseIsHandled();
455 // Step 8.5. Set writer.[[closedPromise]] to a promise rejected with
456 // storedError.
457 RefPtr<Promise> closedPromise =
458 Promise::CreateInfallible(aWriter->GetParentObject());
459 closedPromise->MaybeReject(storedError);
460 aWriter->SetClosedPromise(closedPromise);
462 // Step 8.6 Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
463 closedPromise->SetSettledPromiseIsHandled();
467 // https://streams.spec.whatwg.org/#writable-stream-default-writer-ensure-closed-promise-rejected
468 void WritableStreamDefaultWriterEnsureClosedPromiseRejected(
469 WritableStreamDefaultWriter* aWriter, JS::Handle<JS::Value> aError) {
470 RefPtr<Promise> closedPromise = aWriter->ClosedPromise();
471 // Step 1. If writer.[[closedPromise]].[[PromiseState]] is "pending", reject
472 // writer.[[closedPromise]] with error.
473 if (closedPromise->State() == Promise::PromiseState::Pending) {
474 closedPromise->MaybeReject(aError);
475 } else {
476 // Step 2. Otherwise, set writer.[[closedPromise]] to a promise rejected
477 // with error.
478 closedPromise = Promise::CreateInfallible(aWriter->GetParentObject());
479 closedPromise->MaybeReject(aError);
480 aWriter->SetClosedPromise(closedPromise);
483 // Step 3. Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
484 closedPromise->SetSettledPromiseIsHandled();
487 // https://streams.spec.whatwg.org/#writable-stream-default-writer-ensure-ready-promise-rejected
488 void WritableStreamDefaultWriterEnsureReadyPromiseRejected(
489 WritableStreamDefaultWriter* aWriter, JS::Handle<JS::Value> aError) {
490 RefPtr<Promise> readyPromise = aWriter->ReadyPromise();
491 // Step 1. If writer.[[readyPromise]].[[PromiseState]] is "pending", reject
492 // writer.[[readyPromise]] with error.
493 if (readyPromise->State() == Promise::PromiseState::Pending) {
494 readyPromise->MaybeReject(aError);
495 } else {
496 // Step 2. Otherwise, set writer.[[readyPromise]] to a promise rejected with
497 // error.
498 readyPromise = Promise::CreateInfallible(aWriter->GetParentObject());
499 readyPromise->MaybeReject(aError);
500 aWriter->SetReadyPromise(readyPromise);
503 // Step 3. Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
504 readyPromise->SetSettledPromiseIsHandled();
507 // https://streams.spec.whatwg.org/#writable-stream-default-writer-close-with-error-propagation
508 already_AddRefed<Promise> WritableStreamDefaultWriterCloseWithErrorPropagation(
509 JSContext* aCx, WritableStreamDefaultWriter* aWriter, ErrorResult& aRv) {
510 // Step 1. Let stream be writer.[[stream]].
511 RefPtr<WritableStream> stream = aWriter->GetStream();
513 // Step 2. Assert: stream is not undefined.
514 MOZ_ASSERT(stream);
516 // Step 3. Let state be stream.[[state]].
517 WritableStream::WriterState state = stream->State();
519 // Step 4. If ! WritableStreamCloseQueuedOrInFlight(stream) is true
520 // or state is "closed", return a promise resolved with undefined.
521 if (stream->CloseQueuedOrInFlight() ||
522 state == WritableStream::WriterState::Closed) {
523 return Promise::CreateResolvedWithUndefined(aWriter->GetParentObject(),
524 aRv);
527 // Step 5. If state is "errored",
528 // return a promise rejected with stream.[[storedError]].
529 if (state == WritableStream::WriterState::Errored) {
530 JS::Rooted<JS::Value> error(aCx, stream->StoredError());
531 return Promise::CreateRejected(aWriter->GetParentObject(), error, aRv);
534 // Step 6. Assert: state is "writable" or "erroring".
535 MOZ_ASSERT(state == WritableStream::WriterState::Writable ||
536 state == WritableStream::WriterState::Erroring);
538 // Step 7. Return ! WritableStreamDefaultWriterClose(writer).
539 return WritableStreamDefaultWriterClose(aCx, aWriter, aRv);
542 } // namespace streams_abstract
544 } // namespace mozilla::dom