forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			544 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			544 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim:set ts=2 sw=2 sts=2 et cindent: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "mozilla/dom/WritableStreamDefaultWriter.h"
 | |
| #include "js/Array.h"
 | |
| #include "js/TypeDecls.h"
 | |
| #include "js/Value.h"
 | |
| #include "mozilla/AlreadyAddRefed.h"
 | |
| #include "mozilla/Assertions.h"
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/CycleCollectedJSContext.h"
 | |
| #include "mozilla/HoldDropJSObjects.h"
 | |
| #include "mozilla/dom/WritableStream.h"
 | |
| #include "mozilla/dom/WritableStreamDefaultWriterBinding.h"
 | |
| #include "nsCOMPtr.h"
 | |
| 
 | |
| #include "mozilla/dom/Promise-inl.h"
 | |
| #include "nsIGlobalObject.h"
 | |
| #include "nsISupports.h"
 | |
| 
 | |
| namespace mozilla::dom {
 | |
| 
 | |
| using namespace streams_abstract;
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_CLASS(WritableStreamDefaultWriter)
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WritableStreamDefaultWriter)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mStream, mReadyPromise,
 | |
|                                   mClosedPromise)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WritableStreamDefaultWriter)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mStream, mReadyPromise,
 | |
|                                     mClosedPromise)
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WritableStreamDefaultWriter)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 | |
| NS_IMPL_CYCLE_COLLECTION_TRACE_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStreamDefaultWriter)
 | |
| NS_IMPL_CYCLE_COLLECTING_RELEASE(WritableStreamDefaultWriter)
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableStreamDefaultWriter)
 | |
|   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 | |
|   NS_INTERFACE_MAP_ENTRY(nsISupports)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| WritableStreamDefaultWriter::WritableStreamDefaultWriter(
 | |
|     nsIGlobalObject* aGlobal)
 | |
|     : mGlobal(aGlobal) {
 | |
|   mozilla::HoldJSObjects(this);
 | |
| }
 | |
| 
 | |
| WritableStreamDefaultWriter::~WritableStreamDefaultWriter() {
 | |
|   mozilla::DropJSObjects(this);
 | |
| }
 | |
| 
 | |
| void WritableStreamDefaultWriter::SetReadyPromise(Promise* aPromise) {
 | |
|   MOZ_ASSERT(aPromise);
 | |
|   mReadyPromise = aPromise;
 | |
| }
 | |
| 
 | |
| void WritableStreamDefaultWriter::SetClosedPromise(Promise* aPromise) {
 | |
|   MOZ_ASSERT(aPromise);
 | |
|   mClosedPromise = aPromise;
 | |
| }
 | |
| 
 | |
| JSObject* WritableStreamDefaultWriter::WrapObject(
 | |
|     JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
 | |
|   return WritableStreamDefaultWriter_Binding::Wrap(aCx, this, aGivenProto);
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| already_AddRefed<WritableStreamDefaultWriter>
 | |
| WritableStreamDefaultWriter::Constructor(const GlobalObject& aGlobal,
 | |
|                                          WritableStream& aStream,
 | |
|                                          ErrorResult& aRv) {
 | |
|   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 | |
|   RefPtr<WritableStreamDefaultWriter> writer =
 | |
|       new WritableStreamDefaultWriter(global);
 | |
|   SetUpWritableStreamDefaultWriter(writer, &aStream, aRv);
 | |
|   if (aRv.Failed()) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return writer.forget();
 | |
| }
 | |
| 
 | |
| already_AddRefed<Promise> WritableStreamDefaultWriter::Closed() {
 | |
|   RefPtr<Promise> closedPromise = mClosedPromise;
 | |
|   return closedPromise.forget();
 | |
| }
 | |
| 
 | |
| already_AddRefed<Promise> WritableStreamDefaultWriter::Ready() {
 | |
|   RefPtr<Promise> readyPromise = mReadyPromise;
 | |
|   return readyPromise.forget();
 | |
| }
 | |
| 
 | |
| namespace streams_abstract {
 | |
| // https://streams.spec.whatwg.org/#writable-stream-default-writer-get-desired-size
 | |
| Nullable<double> WritableStreamDefaultWriterGetDesiredSize(
 | |
|     WritableStreamDefaultWriter* aWriter) {
 | |
|   // Step 1. Let stream be writer.[[stream]].
 | |
|   RefPtr<WritableStream> stream = aWriter->GetStream();
 | |
| 
 | |
|   // Step 2. Let state be stream.[[state]].
 | |
|   WritableStream::WriterState state = stream->State();
 | |
| 
 | |
|   // Step 3. If state is "errored" or "erroring", return null.
 | |
|   if (state == WritableStream::WriterState::Errored ||
 | |
|       state == WritableStream::WriterState::Erroring) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Step 4. If state is "closed", return 0.
 | |
|   if (state == WritableStream::WriterState::Closed) {
 | |
|     return 0.0;
 | |
|   }
 | |
| 
 | |
|   // Step 5. Return
 | |
|   // ! WritableStreamDefaultControllerGetDesiredSize(stream.[[controller]]).
 | |
|   return stream->Controller()->GetDesiredSize();
 | |
| }
 | |
| }  // namespace streams_abstract
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#default-writer-desired-size
 | |
| Nullable<double> WritableStreamDefaultWriter::GetDesiredSize(ErrorResult& aRv) {
 | |
|   // Step 1. If this.[[stream]] is undefined, throw a TypeError exception.
 | |
|   if (!mStream) {
 | |
|     aRv.ThrowTypeError("Missing stream");
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Step 2. Return ! WritableStreamDefaultWriterGetDesiredSize(this).
 | |
|   RefPtr<WritableStreamDefaultWriter> thisRefPtr = this;
 | |
|   return WritableStreamDefaultWriterGetDesiredSize(thisRefPtr);
 | |
| }
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#writable-stream-default-writer-abort
 | |
| MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> WritableStreamDefaultWriterAbort(
 | |
|     JSContext* aCx, WritableStreamDefaultWriter* aWriter,
 | |
|     JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
 | |
|   // Step 1. Let stream be writer.[[stream]].
 | |
|   RefPtr<WritableStream> stream = aWriter->GetStream();
 | |
| 
 | |
|   // Step 2. Assert: stream is not undefined.
 | |
|   MOZ_ASSERT(stream);
 | |
| 
 | |
|   // Step 3. Return ! WritableStreamAbort(stream, reason).
 | |
|   return WritableStreamAbort(aCx, stream, aReason, aRv);
 | |
| }
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#default-writer-abort
 | |
| already_AddRefed<Promise> WritableStreamDefaultWriter::Abort(
 | |
|     JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) {
 | |
|   // Step 1. If this.[[stream]] is undefined, return a promise rejected with a
 | |
|   // TypeError exception.
 | |
|   if (!mStream) {
 | |
|     aRv.ThrowTypeError("Missing stream");
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Step 2. Return ! WritableStreamDefaultWriterAbort(this, reason).
 | |
|   RefPtr<WritableStreamDefaultWriter> thisRefPtr = this;
 | |
|   return WritableStreamDefaultWriterAbort(aCx, thisRefPtr, aReason, aRv);
 | |
| }
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#writable-stream-default-writer-close
 | |
| MOZ_CAN_RUN_SCRIPT static already_AddRefed<Promise>
 | |
| WritableStreamDefaultWriterClose(JSContext* aCx,
 | |
|                                  WritableStreamDefaultWriter* aWriter,
 | |
|                                  ErrorResult& aRv) {
 | |
|   // Step 1. Let stream be writer.[[stream]].
 | |
|   RefPtr<WritableStream> stream = aWriter->GetStream();
 | |
| 
 | |
|   // Step 2. Assert: stream is not undefined.
 | |
|   MOZ_ASSERT(stream);
 | |
| 
 | |
|   // Step 3. Return ! WritableStreamClose(stream).
 | |
|   return WritableStreamClose(aCx, stream, aRv);
 | |
| }
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#default-writer-close
 | |
| already_AddRefed<Promise> WritableStreamDefaultWriter::Close(JSContext* aCx,
 | |
|                                                              ErrorResult& aRv) {
 | |
|   // Step 1. Let stream be this.[[stream]].
 | |
|   RefPtr<WritableStream> stream = mStream;
 | |
| 
 | |
|   // Step 2. If stream is undefined, return a promise rejected with a TypeError
 | |
|   // exception.
 | |
|   if (!stream) {
 | |
|     aRv.ThrowTypeError("Missing stream");
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Step 3. If ! WritableStreamCloseQueuedOrInFlight(stream) is true,
 | |
|   // return a promise rejected with a TypeError exception.
 | |
|   if (stream->CloseQueuedOrInFlight()) {
 | |
|     aRv.ThrowTypeError("Stream is closing");
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Step 3. Return ! WritableStreamDefaultWriterClose(this).
 | |
|   RefPtr<WritableStreamDefaultWriter> thisRefPtr = this;
 | |
|   return WritableStreamDefaultWriterClose(aCx, thisRefPtr, aRv);
 | |
| }
 | |
| 
 | |
| namespace streams_abstract {
 | |
| // https://streams.spec.whatwg.org/#writable-stream-default-writer-release
 | |
| void WritableStreamDefaultWriterRelease(JSContext* aCx,
 | |
|                                         WritableStreamDefaultWriter* aWriter) {
 | |
|   // Step 1. Let stream be writer.[[stream]].
 | |
|   RefPtr<WritableStream> stream = aWriter->GetStream();
 | |
| 
 | |
|   // Step 2. Assert: stream is not undefined.
 | |
|   MOZ_ASSERT(stream);
 | |
| 
 | |
|   // Step 3. Assert: stream.[[writer]] is writer.
 | |
|   MOZ_ASSERT(stream->GetWriter() == aWriter);
 | |
| 
 | |
|   // Step 4. Let releasedError be a new TypeError.
 | |
|   JS::Rooted<JS::Value> releasedError(RootingCx(), JS::UndefinedValue());
 | |
|   {
 | |
|     ErrorResult rv;
 | |
|     rv.ThrowTypeError("Releasing lock");
 | |
|     bool ok = ToJSValue(aCx, std::move(rv), &releasedError);
 | |
|     MOZ_RELEASE_ASSERT(ok, "must be ok");
 | |
|   }
 | |
| 
 | |
|   // Step 5. Perform !
 | |
|   // WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer,
 | |
|   // releasedError).
 | |
|   WritableStreamDefaultWriterEnsureReadyPromiseRejected(aWriter, releasedError);
 | |
| 
 | |
|   // Step 6. Perform !
 | |
|   // WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer,
 | |
|   // releasedError).
 | |
|   WritableStreamDefaultWriterEnsureClosedPromiseRejected(aWriter,
 | |
|                                                          releasedError);
 | |
| 
 | |
|   // Step 7. Set stream.[[writer]] to undefined.
 | |
|   stream->SetWriter(nullptr);
 | |
| 
 | |
|   // Step 8. Set writer.[[stream]] to undefined.
 | |
|   aWriter->SetStream(nullptr);
 | |
| }
 | |
| }  // namespace streams_abstract
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#default-writer-release-lock
 | |
| void WritableStreamDefaultWriter::ReleaseLock(JSContext* aCx) {
 | |
|   // Step 1. Let stream be this.[[stream]].
 | |
|   RefPtr<WritableStream> stream = mStream;
 | |
| 
 | |
|   // Step 2. If stream is undefined, return.
 | |
|   if (!stream) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Step 3. Assert: stream.[[writer]] is not undefined.
 | |
|   MOZ_ASSERT(stream->GetWriter());
 | |
| 
 | |
|   // Step 4. Perform ! WritableStreamDefaultWriterRelease(this).
 | |
|   RefPtr<WritableStreamDefaultWriter> thisRefPtr = this;
 | |
|   return WritableStreamDefaultWriterRelease(aCx, thisRefPtr);
 | |
| }
 | |
| 
 | |
| namespace streams_abstract {
 | |
| // https://streams.spec.whatwg.org/#writable-stream-default-writer-write
 | |
| already_AddRefed<Promise> WritableStreamDefaultWriterWrite(
 | |
|     JSContext* aCx, WritableStreamDefaultWriter* aWriter,
 | |
|     JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
 | |
|   // Step 1. Let stream be writer.[[stream]].
 | |
|   RefPtr<WritableStream> stream = aWriter->GetStream();
 | |
| 
 | |
|   // Step 2. Assert: stream is not undefined.
 | |
|   MOZ_ASSERT(stream);
 | |
| 
 | |
|   // Step 3. Let controller be stream.[[controller]].
 | |
|   RefPtr<WritableStreamDefaultController> controller = stream->Controller();
 | |
| 
 | |
|   // Step 4. Let chunkSize be !
 | |
|   // WritableStreamDefaultControllerGetChunkSize(controller, chunk).
 | |
|   double chunkSize =
 | |
|       WritableStreamDefaultControllerGetChunkSize(aCx, controller, aChunk, aRv);
 | |
|   if (aRv.Failed()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Step 5. If stream is not equal to writer.[[stream]], return a promise
 | |
|   // rejected with a TypeError exception.
 | |
|   if (stream != aWriter->GetStream()) {
 | |
|     aRv.ThrowTypeError(
 | |
|         "Can not write on WritableStream owned by another writer.");
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Step 6. Let state be stream.[[state]].
 | |
|   WritableStream::WriterState state = stream->State();
 | |
| 
 | |
|   // Step 7. If state is "errored", return a promise rejected with
 | |
|   // stream.[[storedError]].
 | |
|   if (state == WritableStream::WriterState::Errored) {
 | |
|     JS::Rooted<JS::Value> error(aCx, stream->StoredError());
 | |
|     return Promise::CreateRejected(aWriter->GetParentObject(), error, aRv);
 | |
|   }
 | |
| 
 | |
|   // Step 8. If ! WritableStreamCloseQueuedOrInFlight(stream) is true or state
 | |
|   // is "closed", return a promise rejected with a TypeError exception
 | |
|   // indicating that the stream is closing or closed.
 | |
|   if (stream->CloseQueuedOrInFlight() ||
 | |
|       state == WritableStream::WriterState::Closed) {
 | |
|     return Promise::CreateRejectedWithTypeError(
 | |
|         aWriter->GetParentObject(), "Stream is closed or closing"_ns, aRv);
 | |
|   }
 | |
| 
 | |
|   // Step 9. If state is "erroring", return a promise rejected with
 | |
|   // stream.[[storedError]].
 | |
|   if (state == WritableStream::WriterState::Erroring) {
 | |
|     JS::Rooted<JS::Value> error(aCx, stream->StoredError());
 | |
|     return Promise::CreateRejected(aWriter->GetParentObject(), error, aRv);
 | |
|   }
 | |
| 
 | |
|   // Step 10. Assert: state is "writable".
 | |
|   MOZ_ASSERT(state == WritableStream::WriterState::Writable);
 | |
| 
 | |
|   // Step 11. Let promise be ! WritableStreamAddWriteRequest(stream).
 | |
|   RefPtr<Promise> promise = WritableStreamAddWriteRequest(stream);
 | |
| 
 | |
|   // Step 12. Perform ! WritableStreamDefaultControllerWrite(controller, chunk,
 | |
|   // chunkSize).
 | |
|   WritableStreamDefaultControllerWrite(aCx, controller, aChunk, chunkSize, aRv);
 | |
|   if (aRv.Failed()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Step 13. Return promise.
 | |
|   return promise.forget();
 | |
| }
 | |
| }  // namespace streams_abstract
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#default-writer-write
 | |
| already_AddRefed<Promise> WritableStreamDefaultWriter::Write(
 | |
|     JSContext* aCx, JS::Handle<JS::Value> aChunk, ErrorResult& aRv) {
 | |
|   // Step 1. If this.[[stream]] is undefined, return a promise rejected with a
 | |
|   // TypeError exception.
 | |
|   if (!mStream) {
 | |
|     aRv.ThrowTypeError("Missing stream");
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Step 2. Return ! WritableStreamDefaultWriterWrite(this, chunk).
 | |
|   return WritableStreamDefaultWriterWrite(aCx, this, aChunk, aRv);
 | |
| }
 | |
| 
 | |
| namespace streams_abstract {
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#set-up-writable-stream-default-writer
 | |
| void SetUpWritableStreamDefaultWriter(WritableStreamDefaultWriter* aWriter,
 | |
|                                       WritableStream* aStream,
 | |
|                                       ErrorResult& aRv) {
 | |
|   // Step 1. If ! IsWritableStreamLocked(stream) is true, throw a TypeError
 | |
|   // exception.
 | |
|   if (IsWritableStreamLocked(aStream)) {
 | |
|     aRv.ThrowTypeError("WritableStream is already locked!");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Step 2. Set writer.[[stream]] to stream.
 | |
|   aWriter->SetStream(aStream);
 | |
| 
 | |
|   // Step 3. Set stream.[[writer]] to writer.
 | |
|   aStream->SetWriter(aWriter);
 | |
| 
 | |
|   // Step 4. Let state be stream.[[state]].
 | |
|   WritableStream::WriterState state = aStream->State();
 | |
| 
 | |
|   // Step 5. If state is "writable",
 | |
|   if (state == WritableStream::WriterState::Writable) {
 | |
|     RefPtr<Promise> readyPromise =
 | |
|         Promise::CreateInfallible(aWriter->GetParentObject());
 | |
| 
 | |
|     // Step 5.1 If ! WritableStreamCloseQueuedOrInFlight(stream) is false and
 | |
|     // stream.[[backpressure]] is true, set writer.[[readyPromise]] to a new
 | |
|     // promise.
 | |
|     if (!aStream->CloseQueuedOrInFlight() && aStream->Backpressure()) {
 | |
|       aWriter->SetReadyPromise(readyPromise);
 | |
|     } else {
 | |
|       // Step 5.2. Otherwise, set writer.[[readyPromise]] to a promise resolved
 | |
|       // with undefined.
 | |
|       readyPromise->MaybeResolveWithUndefined();
 | |
|       aWriter->SetReadyPromise(readyPromise);
 | |
|     }
 | |
| 
 | |
|     // Step 5.3. Set writer.[[closedPromise]] to a new promise.
 | |
|     RefPtr<Promise> closedPromise =
 | |
|         Promise::CreateInfallible(aWriter->GetParentObject());
 | |
|     aWriter->SetClosedPromise(closedPromise);
 | |
|   } else if (state == WritableStream::WriterState::Erroring) {
 | |
|     // Step 6. Otherwise, if state is "erroring",
 | |
| 
 | |
|     // Step 6.1. Set writer.[[readyPromise]] to a promise rejected with
 | |
|     // stream.[[storedError]].
 | |
|     JS::Rooted<JS::Value> storedError(RootingCx(), aStream->StoredError());
 | |
|     RefPtr<Promise> readyPromise =
 | |
|         Promise::CreateInfallible(aWriter->GetParentObject());
 | |
|     readyPromise->MaybeReject(storedError);
 | |
|     aWriter->SetReadyPromise(readyPromise);
 | |
| 
 | |
|     // Step 6.2. Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
 | |
|     readyPromise->SetSettledPromiseIsHandled();
 | |
| 
 | |
|     // Step 6.3. Set writer.[[closedPromise]] to a new promise.
 | |
|     RefPtr<Promise> closedPromise =
 | |
|         Promise::CreateInfallible(aWriter->GetParentObject());
 | |
|     aWriter->SetClosedPromise(closedPromise);
 | |
|   } else if (state == WritableStream::WriterState::Closed) {
 | |
|     // Step 7. Otherwise, if state is "closed",
 | |
|     // Step 7.1. Set writer.[[readyPromise]] to a promise resolved with
 | |
|     // undefined.
 | |
|     RefPtr<Promise> readyPromise =
 | |
|         Promise::CreateResolvedWithUndefined(aWriter->GetParentObject(), aRv);
 | |
|     if (aRv.Failed()) {
 | |
|       return;
 | |
|     }
 | |
|     aWriter->SetReadyPromise(readyPromise);
 | |
| 
 | |
|     // Step 7.2. Set writer.[[closedPromise]] to a promise resolved with
 | |
|     // undefined.
 | |
|     RefPtr<Promise> closedPromise =
 | |
|         Promise::CreateResolvedWithUndefined(aWriter->GetParentObject(), aRv);
 | |
|     if (aRv.Failed()) {
 | |
|       return;
 | |
|     }
 | |
|     aWriter->SetClosedPromise(closedPromise);
 | |
|   } else {
 | |
|     // Step 8. Otherwise,
 | |
|     // Step 8.1 Assert: state is "errored".
 | |
|     MOZ_ASSERT(state == WritableStream::WriterState::Errored);
 | |
| 
 | |
|     // Step 8.2. Step Let storedError be stream.[[storedError]].
 | |
|     JS::Rooted<JS::Value> storedError(RootingCx(), aStream->StoredError());
 | |
| 
 | |
|     // Step 8.3. Set writer.[[readyPromise]] to a promise rejected with
 | |
|     // storedError.
 | |
|     RefPtr<Promise> readyPromise =
 | |
|         Promise::CreateInfallible(aWriter->GetParentObject());
 | |
|     readyPromise->MaybeReject(storedError);
 | |
|     aWriter->SetReadyPromise(readyPromise);
 | |
| 
 | |
|     // Step 8.4. Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
 | |
|     readyPromise->SetSettledPromiseIsHandled();
 | |
| 
 | |
|     // Step 8.5. Set writer.[[closedPromise]] to a promise rejected with
 | |
|     // storedError.
 | |
|     RefPtr<Promise> closedPromise =
 | |
|         Promise::CreateInfallible(aWriter->GetParentObject());
 | |
|     closedPromise->MaybeReject(storedError);
 | |
|     aWriter->SetClosedPromise(closedPromise);
 | |
| 
 | |
|     // Step 8.6 Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
 | |
|     closedPromise->SetSettledPromiseIsHandled();
 | |
|   }
 | |
| }
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#writable-stream-default-writer-ensure-closed-promise-rejected
 | |
| void WritableStreamDefaultWriterEnsureClosedPromiseRejected(
 | |
|     WritableStreamDefaultWriter* aWriter, JS::Handle<JS::Value> aError) {
 | |
|   RefPtr<Promise> closedPromise = aWriter->ClosedPromise();
 | |
|   // Step 1. If writer.[[closedPromise]].[[PromiseState]] is "pending", reject
 | |
|   // writer.[[closedPromise]] with error.
 | |
|   if (closedPromise->State() == Promise::PromiseState::Pending) {
 | |
|     closedPromise->MaybeReject(aError);
 | |
|   } else {
 | |
|     // Step 2. Otherwise, set writer.[[closedPromise]] to a promise rejected
 | |
|     // with error.
 | |
|     closedPromise = Promise::CreateInfallible(aWriter->GetParentObject());
 | |
|     closedPromise->MaybeReject(aError);
 | |
|     aWriter->SetClosedPromise(closedPromise);
 | |
|   }
 | |
| 
 | |
|   // Step 3. Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
 | |
|   closedPromise->SetSettledPromiseIsHandled();
 | |
| }
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#writable-stream-default-writer-ensure-ready-promise-rejected
 | |
| void WritableStreamDefaultWriterEnsureReadyPromiseRejected(
 | |
|     WritableStreamDefaultWriter* aWriter, JS::Handle<JS::Value> aError) {
 | |
|   RefPtr<Promise> readyPromise = aWriter->ReadyPromise();
 | |
|   // Step 1. If writer.[[readyPromise]].[[PromiseState]] is "pending", reject
 | |
|   // writer.[[readyPromise]] with error.
 | |
|   if (readyPromise->State() == Promise::PromiseState::Pending) {
 | |
|     readyPromise->MaybeReject(aError);
 | |
|   } else {
 | |
|     // Step 2. Otherwise, set writer.[[readyPromise]] to a promise rejected with
 | |
|     // error.
 | |
|     readyPromise = Promise::CreateInfallible(aWriter->GetParentObject());
 | |
|     readyPromise->MaybeReject(aError);
 | |
|     aWriter->SetReadyPromise(readyPromise);
 | |
|   }
 | |
| 
 | |
|   // Step 3. Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
 | |
|   readyPromise->SetSettledPromiseIsHandled();
 | |
| }
 | |
| 
 | |
| // https://streams.spec.whatwg.org/#writable-stream-default-writer-close-with-error-propagation
 | |
| already_AddRefed<Promise> WritableStreamDefaultWriterCloseWithErrorPropagation(
 | |
|     JSContext* aCx, WritableStreamDefaultWriter* aWriter, ErrorResult& aRv) {
 | |
|   // Step 1. Let stream be writer.[[stream]].
 | |
|   RefPtr<WritableStream> stream = aWriter->GetStream();
 | |
| 
 | |
|   // Step 2. Assert: stream is not undefined.
 | |
|   MOZ_ASSERT(stream);
 | |
| 
 | |
|   // Step 3. Let state be stream.[[state]].
 | |
|   WritableStream::WriterState state = stream->State();
 | |
| 
 | |
|   // Step 4. If ! WritableStreamCloseQueuedOrInFlight(stream) is true
 | |
|   // or state is "closed", return a promise resolved with undefined.
 | |
|   if (stream->CloseQueuedOrInFlight() ||
 | |
|       state == WritableStream::WriterState::Closed) {
 | |
|     return Promise::CreateResolvedWithUndefined(aWriter->GetParentObject(),
 | |
|                                                 aRv);
 | |
|   }
 | |
| 
 | |
|   // Step 5. If state is "errored",
 | |
|   // return a promise rejected with stream.[[storedError]].
 | |
|   if (state == WritableStream::WriterState::Errored) {
 | |
|     JS::Rooted<JS::Value> error(aCx, stream->StoredError());
 | |
|     return Promise::CreateRejected(aWriter->GetParentObject(), error, aRv);
 | |
|   }
 | |
| 
 | |
|   // Step 6. Assert: state is "writable" or "erroring".
 | |
|   MOZ_ASSERT(state == WritableStream::WriterState::Writable ||
 | |
|              state == WritableStream::WriterState::Erroring);
 | |
| 
 | |
|   // Step 7. Return ! WritableStreamDefaultWriterClose(writer).
 | |
|   return WritableStreamDefaultWriterClose(aCx, aWriter, aRv);
 | |
| }
 | |
| 
 | |
| }  // namespace streams_abstract
 | |
| 
 | |
| }  // namespace mozilla::dom
 | 
