WritableStream.cpp (31513B)
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/. */ 6 7 #include "mozilla/dom/WritableStream.h" 8 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/Promise-inl.h" 22 #include "mozilla/dom/QueueWithSizes.h" 23 #include "mozilla/dom/QueuingStrategyBinding.h" 24 #include "mozilla/dom/ReadRequest.h" 25 #include "mozilla/dom/RootedDictionary.h" 26 #include "mozilla/dom/UnderlyingSinkBinding.h" 27 #include "mozilla/dom/WritableStreamBinding.h" 28 #include "mozilla/dom/WritableStreamDefaultController.h" 29 #include "mozilla/dom/WritableStreamDefaultWriter.h" 30 #include "nsCOMPtr.h" 31 #include "nsIGlobalObject.h" 32 #include "nsISupports.h" 33 34 namespace mozilla::dom { 35 36 using namespace streams_abstract; 37 38 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WITH_JS_MEMBERS( 39 WritableStream, 40 (mGlobal, mCloseRequest, mController, mInFlightWriteRequest, 41 mInFlightCloseRequest, mPendingAbortRequestPromise, mWriter, 42 mWriteRequests), 43 (mPendingAbortRequestReason, mStoredError)) 44 45 NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStream) 46 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(WritableStream, 47 LastRelease()) 48 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableStream) 49 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 50 NS_INTERFACE_MAP_ENTRY(nsISupports) 51 NS_INTERFACE_MAP_END 52 53 WritableStream::WritableStream(nsIGlobalObject* aGlobal, 54 HoldDropJSObjectsCaller aHoldDropCaller) 55 : mGlobal(aGlobal), mHoldDropCaller(aHoldDropCaller) { 56 if (mHoldDropCaller == HoldDropJSObjectsCaller::Implicit) { 57 mozilla::HoldJSObjects(this); 58 } 59 } 60 61 WritableStream::WritableStream(const GlobalObject& aGlobal, 62 HoldDropJSObjectsCaller aHoldDropCaller) 63 : mGlobal(do_QueryInterface(aGlobal.GetAsSupports())), 64 mHoldDropCaller(aHoldDropCaller) { 65 if (mHoldDropCaller == HoldDropJSObjectsCaller::Implicit) { 66 mozilla::HoldJSObjects(this); 67 } 68 } 69 70 WritableStream::~WritableStream() { 71 if (mHoldDropCaller == HoldDropJSObjectsCaller::Implicit) { 72 mozilla::DropJSObjects(this); 73 } 74 } 75 76 JSObject* WritableStream::WrapObject(JSContext* aCx, 77 JS::Handle<JSObject*> aGivenProto) { 78 return WritableStream_Binding::Wrap(aCx, this, aGivenProto); 79 } 80 81 // https://streams.spec.whatwg.org/#writable-stream-deal-with-rejection 82 void WritableStream::DealWithRejection(JSContext* aCx, 83 JS::Handle<JS::Value> aError, 84 ErrorResult& aRv) { 85 // Step 1. Let state be stream.[[state]]. 86 // Step 2. If state is "writable", 87 if (mState == WriterState::Writable) { 88 // Step 2.1. Perform ! WritableStreamStartErroring(stream, error). 89 StartErroring(aCx, aError, aRv); 90 91 // Step 2.2. Return. 92 return; 93 } 94 95 // Step 3. Assert: state is "erroring". 96 MOZ_ASSERT(mState == WriterState::Erroring); 97 98 // Step 4. Perform ! WritableStreamFinishErroring(stream). 99 FinishErroring(aCx, aRv); 100 } 101 102 // https://streams.spec.whatwg.org/#writable-stream-finish-erroring 103 void WritableStream::FinishErroring(JSContext* aCx, ErrorResult& aRv) { 104 // Step 1. Assert: stream.[[state]] is "erroring". 105 MOZ_ASSERT(mState == WriterState::Erroring); 106 107 // Step 2. Assert: ! WritableStreamHasOperationMarkedInFlight(stream) is 108 // false. 109 MOZ_ASSERT(!HasOperationMarkedInFlight()); 110 111 // Step 3. Set stream.[[state]] to "errored". 112 mState = WriterState::Errored; 113 114 // Step 4. Perform ! stream.[[controller]].[[ErrorSteps]](). 115 Controller()->ErrorSteps(); 116 117 // Step 5. Let storedError be stream.[[storedError]]. 118 JS::Rooted<JS::Value> storedError(aCx, mStoredError); 119 120 // Step 6. For each writeRequest of stream.[[writeRequests]]: 121 while (!mWriteRequests.IsEmpty()) { 122 // Step 6.1. Reject writeRequest with storedError. 123 mWriteRequests.Pop()->MaybeReject(storedError); 124 } 125 126 // Step 7. Set stream.[[writeRequests]] to an empty list. 127 // (Popping should make it empty) 128 129 // Step 8. If stream.[[pendingAbortRequest]] is undefined, 130 if (!mPendingAbortRequestPromise) { 131 // Step 8.1. Perform ! 132 // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream). 133 RejectCloseAndClosedPromiseIfNeeded(); 134 135 // Step 8.2. Return. 136 return; 137 } 138 139 // Step 9. Let abortRequest be stream.[[pendingAbortRequest]]. 140 RefPtr<Promise> abortPromise = mPendingAbortRequestPromise; 141 JS::Rooted<JS::Value> abortReason(aCx, mPendingAbortRequestReason); 142 bool abortWasAlreadyErroring = mPendingAbortRequestWasAlreadyErroring; 143 144 // Step 10. Set stream.[[pendingAbortRequest]] to undefined. 145 SetPendingAbortRequest(nullptr, JS::UndefinedHandleValue, false); 146 147 // Step 11. If abortRequest’s was already erroring is true, 148 if (abortWasAlreadyErroring) { 149 // Step 11.1. Reject abortRequest’s promise with storedError. 150 abortPromise->MaybeReject(storedError); 151 152 // Step 11.2. Perform ! 153 // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream). 154 RejectCloseAndClosedPromiseIfNeeded(); 155 156 // Step 11.3. Return. 157 return; 158 } 159 160 // Step 12. Let promise be ! 161 // stream.[[controller]].[[AbortSteps]](abortRequest’s reason). 162 RefPtr<WritableStreamDefaultController> controller = mController; 163 RefPtr<Promise> promise = controller->AbortSteps(aCx, abortReason, aRv); 164 if (aRv.Failed()) { 165 return; 166 } 167 168 // Step 13 + 14. 169 promise->AddCallbacksWithCycleCollectedArgs( 170 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv, 171 Promise* aAbortRequestPromise, WritableStream* aStream) { 172 // Step 13. Upon fulfillment of promise, 173 // Step 13.1. Resolve abortRequest’s promise with undefined. 174 aAbortRequestPromise->MaybeResolveWithUndefined(); 175 176 // Step 13.2. Perform ! 177 // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream). 178 aStream->RejectCloseAndClosedPromiseIfNeeded(); 179 }, 180 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv, 181 Promise* aAbortRequestPromise, WritableStream* aStream) { 182 // Step 14. Upon rejection of promise with reason reason, 183 // Step 14.1. Reject abortRequest’s promise with reason. 184 aAbortRequestPromise->MaybeReject(aValue); 185 186 // Step 14.2. Perform ! 187 // WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream). 188 aStream->RejectCloseAndClosedPromiseIfNeeded(); 189 }, 190 RefPtr(abortPromise), RefPtr(this)); 191 } 192 193 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close 194 void WritableStream::FinishInFlightClose() { 195 // Step 1. Assert: stream.[[inFlightCloseRequest]] is not undefined. 196 MOZ_ASSERT(mInFlightCloseRequest); 197 198 // Step 2. Resolve stream.[[inFlightCloseRequest]] with undefined. 199 mInFlightCloseRequest->MaybeResolveWithUndefined(); 200 201 // Step 3. Set stream.[[inFlightCloseRequest]] to undefined. 202 mInFlightCloseRequest = nullptr; 203 204 // Step 4. Let state be stream.[[state]]. 205 // Step 5. Assert: stream.[[state]] is "writable" or "erroring". 206 MOZ_ASSERT(mState == WriterState::Writable || 207 mState == WriterState::Erroring); 208 209 // Step 6. If state is "erroring", 210 if (mState == WriterState::Erroring) { 211 // Step 6.1. Set stream.[[storedError]] to undefined. 212 mStoredError.setUndefined(); 213 214 // Step 6.2. If stream.[[pendingAbortRequest]] is not undefined, 215 if (mPendingAbortRequestPromise) { 216 // Step 6.2.1. Resolve stream.[[pendingAbortRequest]]'s promise with 217 // undefined. 218 mPendingAbortRequestPromise->MaybeResolveWithUndefined(); 219 220 // Step 6.2.2. Set stream.[[pendingAbortRequest]] to undefined. 221 SetPendingAbortRequest(nullptr, JS::UndefinedHandleValue, false); 222 } 223 } 224 225 // Step 7. Set stream.[[state]] to "closed". 226 mState = WriterState::Closed; 227 228 // Step 8. Let writer be stream.[[writer]]. 229 // Step 9. If writer is not undefined, resolve writer.[[closedPromise]] with 230 // undefined. 231 if (mWriter) { 232 mWriter->ClosedPromise()->MaybeResolveWithUndefined(); 233 } 234 235 // Step 10. Assert: stream.[[pendingAbortRequest]] is undefined. 236 MOZ_ASSERT(!mPendingAbortRequestPromise); 237 // Assert: stream.[[storedError]] is undefined. 238 MOZ_ASSERT(mStoredError.isUndefined()); 239 } 240 241 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close-with-error 242 void WritableStream::FinishInFlightCloseWithError(JSContext* aCx, 243 JS::Handle<JS::Value> aError, 244 ErrorResult& aRv) { 245 // Step 1. Assert: stream.[[inFlightCloseRequest]] is not undefined. 246 MOZ_ASSERT(mInFlightCloseRequest); 247 248 // Step 2. Reject stream.[[inFlightCloseRequest]] with error. 249 mInFlightCloseRequest->MaybeReject(aError); 250 251 // Step 3. Set stream.[[inFlightCloseRequest]] to undefined. 252 mInFlightCloseRequest = nullptr; 253 254 // Step 4. Assert: stream.[[state]] is "writable" or "erroring". 255 MOZ_ASSERT(mState == WriterState::Writable || 256 mState == WriterState::Erroring); 257 258 // Step 5. If stream.[[pendingAbortRequest]] is not undefined, 259 if (mPendingAbortRequestPromise) { 260 // Step 5.1. Reject stream.[[pendingAbortRequest]]'s promise with error. 261 mPendingAbortRequestPromise->MaybeReject(aError); 262 263 // Step 5.2. Set stream.[[pendingAbortRequest]] to undefined. 264 SetPendingAbortRequest(nullptr, JS::UndefinedHandleValue, false); 265 } 266 267 // Step 6. Perform ! WritableStreamDealWithRejection(stream, error). 268 DealWithRejection(aCx, aError, aRv); 269 } 270 271 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write 272 void WritableStream::FinishInFlightWrite() { 273 // Step 1. Assert: stream.[[inFlightWriteRequest]] is not undefined. 274 MOZ_ASSERT(mInFlightWriteRequest); 275 276 // Step 2. Resolve stream.[[inFlightWriteRequest]] with undefined. 277 mInFlightWriteRequest->MaybeResolveWithUndefined(); 278 279 // Step 3. Set stream.[[inFlightWriteRequest]] to undefined. 280 mInFlightWriteRequest = nullptr; 281 } 282 283 // https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write-with-error 284 void WritableStream::FinishInFlightWriteWithError(JSContext* aCx, 285 JS::Handle<JS::Value> aError, 286 ErrorResult& aRv) { 287 // Step 1. Assert: stream.[[inFlightWriteRequest]] is not undefined. 288 MOZ_ASSERT(mInFlightWriteRequest); 289 290 // Step 2. Reject stream.[[inFlightWriteRequest]] with error. 291 mInFlightWriteRequest->MaybeReject(aError); 292 293 // Step 3. Set stream.[[inFlightWriteRequest]] to undefined. 294 mInFlightWriteRequest = nullptr; 295 296 // Step 4. Assert: stream.[[state]] is "writable" or "erroring". 297 MOZ_ASSERT(mState == WriterState::Writable || 298 mState == WriterState::Erroring); 299 300 // Step 5. Perform ! WritableStreamDealWithRejection(stream, error). 301 DealWithRejection(aCx, aError, aRv); 302 } 303 304 // https://streams.spec.whatwg.org/#writable-stream-mark-close-request-in-flight 305 void WritableStream::MarkCloseRequestInFlight() { 306 // Step 1. Assert: stream.[[inFlightCloseRequest]] is undefined. 307 MOZ_ASSERT(!mInFlightCloseRequest); 308 309 // Step 2. Assert: stream.[[closeRequest]] is not undefined. 310 MOZ_ASSERT(mCloseRequest); 311 312 // Step 3. Set stream.[[inFlightCloseRequest]] to stream.[[closeRequest]]. 313 mInFlightCloseRequest = mCloseRequest; 314 315 // Step 4. Set stream.[[closeRequest]] to undefined. 316 mCloseRequest = nullptr; 317 } 318 319 // https://streams.spec.whatwg.org/#writable-stream-mark-first-write-request-in-flight 320 void WritableStream::MarkFirstWriteRequestInFlight() { 321 // Step 1. Assert: stream.[[inFlightWriteRequest]] is undefined. 322 MOZ_ASSERT(!mInFlightWriteRequest); 323 324 // Step 2. Assert: stream.[[writeRequests]] is not empty. 325 MOZ_ASSERT(!mWriteRequests.IsEmpty()); 326 327 // Step 3. Let writeRequest be stream.[[writeRequests]][0]. 328 // Step 4. Remove writeRequest from stream.[[writeRequests]]. 329 RefPtr<Promise> writeRequest = mWriteRequests.Pop(); 330 331 // Step 5. Set stream.[[inFlightWriteRequest]] to writeRequest. 332 mInFlightWriteRequest = writeRequest; 333 } 334 335 // https://streams.spec.whatwg.org/#writable-stream-reject-close-and-closed-promise-if-needed 336 void WritableStream::RejectCloseAndClosedPromiseIfNeeded() { 337 // Step 1. Assert: stream.[[state]] is "errored". 338 MOZ_ASSERT(mState == WriterState::Errored); 339 340 JS::Rooted<JS::Value> storedError(RootingCx(), mStoredError); 341 // Step 2. If stream.[[closeRequest]] is not undefined, 342 if (mCloseRequest) { 343 // Step 2.1. Assert: stream.[[inFlightCloseRequest]] is undefined. 344 MOZ_ASSERT(!mInFlightCloseRequest); 345 346 // Step 2.2. Reject stream.[[closeRequest]] with stream.[[storedError]]. 347 mCloseRequest->MaybeReject(storedError); 348 349 // Step 2.3. Set stream.[[closeRequest]] to undefined. 350 mCloseRequest = nullptr; 351 } 352 353 // Step 3. Let writer be stream.[[writer]]. 354 RefPtr<WritableStreamDefaultWriter> writer = mWriter; 355 356 // Step 4. If writer is not undefined, 357 if (writer) { 358 // Step 4.1. Reject writer.[[closedPromise]] with stream.[[storedError]]. 359 RefPtr<Promise> closedPromise = writer->ClosedPromise(); 360 closedPromise->MaybeReject(storedError); 361 362 // Step 4.2. Set writer.[[closedPromise]].[[PromiseIsHandled]] to true. 363 closedPromise->SetSettledPromiseIsHandled(); 364 } 365 } 366 367 // https://streams.spec.whatwg.org/#writable-stream-start-erroring 368 void WritableStream::StartErroring(JSContext* aCx, 369 JS::Handle<JS::Value> aReason, 370 ErrorResult& aRv) { 371 // Step 1. Assert: stream.[[storedError]] is undefined. 372 MOZ_ASSERT(mStoredError.isUndefined()); 373 374 // Step 2. Assert: stream.[[state]] is "writable". 375 MOZ_ASSERT(mState == WriterState::Writable); 376 377 // Step 3. Let controller be stream.[[controller]]. 378 RefPtr<WritableStreamDefaultController> controller = mController; 379 // Step 4. Assert: controller is not undefined. 380 MOZ_ASSERT(controller); 381 382 // Step 5. Set stream.[[state]] to "erroring". 383 mState = WriterState::Erroring; 384 385 // Step 6. Set stream.[[storedError]] to reason. 386 mStoredError = aReason; 387 388 // Step 7. Let writer be stream.[[writer]]. 389 RefPtr<WritableStreamDefaultWriter> writer = mWriter; 390 // Step 8. If writer is not undefined, perform ! 391 // WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason). 392 if (writer) { 393 WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, aReason); 394 } 395 396 // Step 9. If ! WritableStreamHasOperationMarkedInFlight(stream) is false 397 // and controller.[[started]] is true, 398 // perform !WritableStreamFinishErroring(stream). 399 if (!HasOperationMarkedInFlight() && controller->Started()) { 400 FinishErroring(aCx, aRv); 401 } 402 } 403 404 // https://streams.spec.whatwg.org/#writable-stream-update-backpressure 405 void WritableStream::UpdateBackpressure(bool aBackpressure) { 406 // Step 1. Assert: stream.[[state]] is "writable". 407 MOZ_ASSERT(mState == WriterState::Writable); 408 // Step 2. Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false. 409 MOZ_ASSERT(!CloseQueuedOrInFlight()); 410 411 // Step 3. Let writer be stream.[[writer]]. 412 RefPtr<WritableStreamDefaultWriter> writer = mWriter; 413 414 // Step 4. If writer is not undefined and backpressure is not 415 // stream.[[backpressure]], 416 if (writer && aBackpressure != mBackpressure) { 417 // Step 4.1. If backpressure is true, set writer.[[readyPromise]] to a new 418 // promise. 419 if (aBackpressure) { 420 RefPtr<Promise> promise = 421 Promise::CreateInfallible(writer->GetParentObject()); 422 writer->SetReadyPromise(promise); 423 } else { 424 // Step 4.2. Otherwise, 425 // Step 4.2.1. Assert: backpressure is false. 426 // Step 4.2.2. Resolve writer.[[readyPromise]] with undefined. 427 writer->ReadyPromise()->MaybeResolveWithUndefined(); 428 } 429 } 430 431 // Step 5. Set stream.[[backpressure]] to backpressure. 432 mBackpressure = aBackpressure; 433 } 434 435 // https://streams.spec.whatwg.org/#ws-constructor 436 already_AddRefed<WritableStream> WritableStream::Constructor( 437 const GlobalObject& aGlobal, 438 const Optional<JS::Handle<JSObject*>>& aUnderlyingSink, 439 const QueuingStrategy& aStrategy, ErrorResult& aRv) { 440 // Step 1. If underlyingSink is missing, set it to null. 441 JS::Rooted<JSObject*> underlyingSinkObj( 442 aGlobal.Context(), 443 aUnderlyingSink.WasPassed() ? aUnderlyingSink.Value() : nullptr); 444 445 // Step 2. Let underlyingSinkDict be underlyingSink, converted to 446 // an IDL value of type UnderlyingSink. 447 RootedDictionary<UnderlyingSink> underlyingSinkDict(aGlobal.Context()); 448 if (underlyingSinkObj) { 449 JS::Rooted<JS::Value> objValue(aGlobal.Context(), 450 JS::ObjectValue(*underlyingSinkObj)); 451 dom::BindingCallContext callCx(aGlobal.Context(), 452 "WritableStream.constructor"); 453 aRv.MightThrowJSException(); 454 if (!underlyingSinkDict.Init(callCx, objValue)) { 455 aRv.StealExceptionFromJSContext(aGlobal.Context()); 456 return nullptr; 457 } 458 } 459 460 // Step 3. If underlyingSinkDict["type"] exists, throw a RangeError exception. 461 if (!underlyingSinkDict.mType.isUndefined()) { 462 aRv.ThrowRangeError("Implementation preserved member 'type'"); 463 return nullptr; 464 } 465 466 // Step 4. Perform ! InitializeWritableStream(this). 467 RefPtr<WritableStream> writableStream = 468 new WritableStream(aGlobal, HoldDropJSObjectsCaller::Implicit); 469 470 // Step 5. Let sizeAlgorithm be ! ExtractSizeAlgorithm(strategy). 471 // 472 // Implementation Note: The specification demands that if the size doesn't 473 // exist, we instead would provide an algorithm that returns 1. Instead, we 474 // will teach callers that a missing callback should simply return 1, rather 475 // than gin up a fake callback here. 476 // 477 // This decision may need to be revisited if the default action ever diverges 478 // within the specification. 479 RefPtr<QueuingStrategySize> sizeAlgorithm = 480 aStrategy.mSize.WasPassed() ? &aStrategy.mSize.Value() : nullptr; 481 482 // Step 6. Let highWaterMark be ? ExtractHighWaterMark(strategy, 1). 483 double highWaterMark = ExtractHighWaterMark(aStrategy, 1, aRv); 484 if (aRv.Failed()) { 485 return nullptr; 486 } 487 488 // Step 7. Perform ? SetUpWritableStreamDefaultControllerFromUnderlyingSink( 489 // this, underlyingSink, underlyingSinkDict, highWaterMark, sizeAlgorithm). 490 SetUpWritableStreamDefaultControllerFromUnderlyingSink( 491 aGlobal.Context(), writableStream, underlyingSinkObj, underlyingSinkDict, 492 highWaterMark, sizeAlgorithm, aRv); 493 if (aRv.Failed()) { 494 return nullptr; 495 } 496 497 return writableStream.forget(); 498 } 499 500 namespace streams_abstract { 501 // https://streams.spec.whatwg.org/#writable-stream-abort 502 already_AddRefed<Promise> WritableStreamAbort(JSContext* aCx, 503 WritableStream* aStream, 504 JS::Handle<JS::Value> aReason, 505 ErrorResult& aRv) { 506 // Step 1. If stream.[[state]] is "closed" or "errored", return a promise 507 // resolved with undefined. 508 if (aStream->State() == WritableStream::WriterState::Closed || 509 aStream->State() == WritableStream::WriterState::Errored) { 510 RefPtr<Promise> promise = 511 Promise::CreateInfallible(aStream->GetParentObject()); 512 promise->MaybeResolveWithUndefined(); 513 return promise.forget(); 514 } 515 516 // Step 2. Signal abort on stream.[[controller]].[[signal]] with reason. 517 RefPtr<WritableStreamDefaultController> controller = aStream->Controller(); 518 controller->Signal()->SignalAbort(aReason); 519 520 // Step 3. Let state be stream.[[state]]. 521 WritableStream::WriterState state = aStream->State(); 522 523 // Step 4. If state is "closed" or "errored", return a promise resolved with 524 // undefined. Note: We re-check the state because signaling abort runs author 525 // code and that might have changed the state. 526 if (aStream->State() == WritableStream::WriterState::Closed || 527 aStream->State() == WritableStream::WriterState::Errored) { 528 RefPtr<Promise> promise = 529 Promise::CreateInfallible(aStream->GetParentObject()); 530 promise->MaybeResolveWithUndefined(); 531 return promise.forget(); 532 } 533 534 // Step 5. If stream.[[pendingAbortRequest]] is not undefined, return 535 // stream.[[pendingAbortRequest]]'s promise. 536 if (aStream->GetPendingAbortRequestPromise()) { 537 RefPtr<Promise> promise = aStream->GetPendingAbortRequestPromise(); 538 return promise.forget(); 539 } 540 541 // Step 6. Assert: state is "writable" or "erroring". 542 MOZ_ASSERT(state == WritableStream::WriterState::Writable || 543 state == WritableStream::WriterState::Erroring); 544 545 // Step 7. Let wasAlreadyErroring be false. 546 bool wasAlreadyErroring = false; 547 548 // Step 8. If state is "erroring", 549 JS::Rooted<JS::Value> reason(aCx, aReason); 550 if (state == WritableStream::WriterState::Erroring) { 551 // Step 8.1. Set wasAlreadyErroring to true. 552 wasAlreadyErroring = true; 553 // Step 8.2. Set reason to undefined. 554 reason.setUndefined(); 555 } 556 557 // Step 9. Let promise be a new promise. 558 RefPtr<Promise> promise = 559 Promise::CreateInfallible(aStream->GetParentObject()); 560 561 // Step 10. Set stream.[[pendingAbortRequest]] to a new pending abort request 562 // whose promise is promise, reason is reason, and was already erroring is 563 // wasAlreadyErroring. 564 aStream->SetPendingAbortRequest(promise, reason, wasAlreadyErroring); 565 566 // Step 11. If wasAlreadyErroring is false, perform ! 567 // WritableStreamStartErroring(stream, reason). 568 if (!wasAlreadyErroring) { 569 aStream->StartErroring(aCx, reason, aRv); 570 if (aRv.Failed()) { 571 return nullptr; 572 } 573 } 574 575 // Step 12. Return promise. 576 return promise.forget(); 577 } 578 } // namespace streams_abstract 579 580 // https://streams.spec.whatwg.org/#ws-abort 581 already_AddRefed<Promise> WritableStream::Abort(JSContext* aCx, 582 JS::Handle<JS::Value> aReason, 583 ErrorResult& aRv) { 584 // Step 1. If ! IsWritableStreamLocked(this) is true, return a promise 585 // rejected with a TypeError exception. 586 if (Locked()) { 587 return Promise::CreateRejectedWithTypeError( 588 GetParentObject(), "Canceled Locked Stream"_ns, aRv); 589 } 590 591 // Step 2. Return ! WritableStreamAbort(this, reason). 592 RefPtr<WritableStream> thisRefPtr = this; 593 return WritableStreamAbort(aCx, thisRefPtr, aReason, aRv); 594 } 595 596 namespace streams_abstract { 597 // https://streams.spec.whatwg.org/#writable-stream-close 598 already_AddRefed<Promise> WritableStreamClose(JSContext* aCx, 599 WritableStream* aStream, 600 ErrorResult& aRv) { 601 // Step 1. Let state be stream.[[state]]. 602 WritableStream::WriterState state = aStream->State(); 603 604 // Step 2. If state is "closed" or "errored", return a promise rejected with a 605 // TypeError exception. 606 if (state == WritableStream::WriterState::Closed || 607 state == WritableStream::WriterState::Errored) { 608 return Promise::CreateRejectedWithTypeError( 609 aStream->GetParentObject(), 610 "Can not close stream after closing or error"_ns, aRv); 611 } 612 613 // Step 3. Assert: state is "writable" or "erroring". 614 MOZ_ASSERT(state == WritableStream::WriterState::Writable || 615 state == WritableStream::WriterState::Erroring); 616 617 // Step 4. Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false. 618 MOZ_ASSERT(!aStream->CloseQueuedOrInFlight()); 619 620 // Step 5. Let promise be a new promise. 621 RefPtr<Promise> promise = 622 Promise::CreateInfallible(aStream->GetParentObject()); 623 624 // Step 6. Set stream.[[closeRequest]] to promise. 625 aStream->SetCloseRequest(promise); 626 627 // Step 7. Let writer be stream.[[writer]]. 628 RefPtr<WritableStreamDefaultWriter> writer = aStream->GetWriter(); 629 630 // Step 8. If writer is not undefined, and stream.[[backpressure]] is true, 631 // and state is "writable", resolve writer.[[readyPromise]] with undefined. 632 if (writer && aStream->Backpressure() && 633 state == WritableStream::WriterState::Writable) { 634 writer->ReadyPromise()->MaybeResolveWithUndefined(); 635 } 636 637 // Step 9. 638 // Perform ! WritableStreamDefaultControllerClose(stream.[[controller]]). 639 RefPtr<WritableStreamDefaultController> controller = aStream->Controller(); 640 WritableStreamDefaultControllerClose(aCx, controller, aRv); 641 if (aRv.Failed()) { 642 return nullptr; 643 } 644 645 // Step 10. Return promise. 646 return promise.forget(); 647 } 648 } // namespace streams_abstract 649 650 // https://streams.spec.whatwg.org/#ws-close 651 already_AddRefed<Promise> WritableStream::Close(JSContext* aCx, 652 ErrorResult& aRv) { 653 // Step 1. If ! IsWritableStreamLocked(this) is true, return a promise 654 // rejected with a TypeError exception. 655 if (Locked()) { 656 return Promise::CreateRejectedWithTypeError( 657 GetParentObject(), "Can not close locked stream"_ns, aRv); 658 } 659 660 // Step 2. If ! WritableStreamCloseQueuedOrInFlight(this) is true, return a 661 // promise rejected with a TypeError exception. 662 if (CloseQueuedOrInFlight()) { 663 return Promise::CreateRejectedWithTypeError( 664 GetParentObject(), "Stream is already closing"_ns, aRv); 665 } 666 667 // Step 3. Return ! WritableStreamClose(this). 668 RefPtr<WritableStream> thisRefPtr = this; 669 return WritableStreamClose(aCx, thisRefPtr, aRv); 670 } 671 672 namespace streams_abstract { 673 // https://streams.spec.whatwg.org/#acquire-writable-stream-default-writer 674 already_AddRefed<WritableStreamDefaultWriter> 675 AcquireWritableStreamDefaultWriter(WritableStream* aStream, ErrorResult& aRv) { 676 // Step 1. Let writer be a new WritableStreamDefaultWriter. 677 RefPtr<WritableStreamDefaultWriter> writer = 678 new WritableStreamDefaultWriter(aStream->GetParentObject()); 679 680 // Step 2. Perform ? SetUpWritableStreamDefaultWriter(writer, stream). 681 SetUpWritableStreamDefaultWriter(writer, aStream, aRv); 682 if (aRv.Failed()) { 683 return nullptr; 684 } 685 686 // Step 3. Return writer. 687 return writer.forget(); 688 } 689 } // namespace streams_abstract 690 691 // https://streams.spec.whatwg.org/#create-writable-stream 692 already_AddRefed<WritableStream> WritableStream::CreateAbstract( 693 JSContext* aCx, nsIGlobalObject* aGlobal, 694 UnderlyingSinkAlgorithmsBase* aAlgorithms, double aHighWaterMark, 695 QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) { 696 // Step 1: Assert: ! IsNonNegativeNumber(highWaterMark) is true. 697 MOZ_ASSERT(IsNonNegativeNumber(aHighWaterMark)); 698 699 // Step 2: Let stream be a new WritableStream. 700 // Step 3: Perform ! InitializeWritableStream(stream). 701 RefPtr<WritableStream> stream = new WritableStream( 702 aGlobal, WritableStream::HoldDropJSObjectsCaller::Implicit); 703 704 // Step 4: Let controller be a new WritableStreamDefaultController. 705 auto controller = 706 MakeRefPtr<WritableStreamDefaultController>(aGlobal, *stream); 707 708 // Step 5: Perform ? SetUpWritableStreamDefaultController(stream, controller, 709 // startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, 710 // highWaterMark, sizeAlgorithm). 711 SetUpWritableStreamDefaultController(aCx, stream, controller, aAlgorithms, 712 aHighWaterMark, aSizeAlgorithm, aRv); 713 if (aRv.Failed()) { 714 return nullptr; 715 } 716 717 // Step 6: Return stream. 718 return stream.forget(); 719 } 720 721 already_AddRefed<WritableStreamDefaultWriter> WritableStream::GetWriter( 722 ErrorResult& aRv) { 723 return AcquireWritableStreamDefaultWriter(this, aRv); 724 } 725 726 namespace streams_abstract { 727 // https://streams.spec.whatwg.org/#writable-stream-add-write-request 728 already_AddRefed<Promise> WritableStreamAddWriteRequest( 729 WritableStream* aStream) { 730 // Step 1. Assert: ! IsWritableStreamLocked(stream) is true. 731 MOZ_ASSERT(IsWritableStreamLocked(aStream)); 732 733 // Step 2. Assert: stream.[[state]] is "writable". 734 MOZ_ASSERT(aStream->State() == WritableStream::WriterState::Writable); 735 736 // Step 3. Let promise be a new promise. 737 RefPtr<Promise> promise = 738 Promise::CreateInfallible(aStream->GetParentObject()); 739 740 // Step 4. Append promise to stream.[[writeRequests]]. 741 aStream->AppendWriteRequest(promise); 742 743 // Step 5. Return promise. 744 return promise.forget(); 745 } 746 } // namespace streams_abstract 747 748 // https://streams.spec.whatwg.org/#writablestream-set-up 749 // _BOUNDARY because `aAlgorithms->StartCallback` (called by 750 // SetUpWritableStreamDefaultController below) should not be able to run script 751 // in this case. 752 MOZ_CAN_RUN_SCRIPT_BOUNDARY void WritableStream::SetUpNative( 753 JSContext* aCx, UnderlyingSinkAlgorithmsWrapper& aAlgorithms, 754 Maybe<double> aHighWaterMark, QueuingStrategySize* aSizeAlgorithm, 755 ErrorResult& aRv) { 756 // an optional number highWaterMark (default 1) 757 double highWaterMark = aHighWaterMark.valueOr(1); 758 // and if given, highWaterMark must be a non-negative, non-NaN number. 759 MOZ_ASSERT(IsNonNegativeNumber(highWaterMark)); 760 761 // Step 1: Let startAlgorithm be an algorithm that returns undefined. 762 // Step 2: Let closeAlgorithmWrapper be an algorithm that runs these steps: 763 // Step 3: Let abortAlgorithmWrapper be an algorithm that runs these steps: 764 // (Covered by UnderlyingSinkAlgorithmsWrapper) 765 766 // Step 4: If sizeAlgorithm was not given, then set it to an algorithm that 767 // returns 1. (Callers will treat nullptr as such, see 768 // WritableStream::Constructor for details) 769 770 // Step 5: Perform ! InitializeWritableStream(stream). 771 // (Covered by the constructor) 772 773 // Step 6: Let controller be a new WritableStreamDefaultController. 774 auto controller = 775 MakeRefPtr<WritableStreamDefaultController>(GetParentObject(), *this); 776 777 // Step 7: Perform ! SetUpWritableStreamDefaultController(stream, controller, 778 // startAlgorithm, writeAlgorithm, closeAlgorithmWrapper, 779 // abortAlgorithmWrapper, highWaterMark, sizeAlgorithm). 780 SetUpWritableStreamDefaultController(aCx, this, controller, &aAlgorithms, 781 highWaterMark, aSizeAlgorithm, aRv); 782 } 783 784 already_AddRefed<WritableStream> WritableStream::CreateNative( 785 JSContext* aCx, nsIGlobalObject& aGlobal, 786 UnderlyingSinkAlgorithmsWrapper& aAlgorithms, Maybe<double> aHighWaterMark, 787 QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) { 788 RefPtr<WritableStream> stream = new WritableStream( 789 &aGlobal, WritableStream::HoldDropJSObjectsCaller::Implicit); 790 stream->SetUpNative(aCx, aAlgorithms, aHighWaterMark, aSizeAlgorithm, aRv); 791 if (aRv.Failed()) { 792 return nullptr; 793 } 794 return stream.forget(); 795 } 796 797 // https://streams.spec.whatwg.org/#writablestream-error 798 // To error a WritableStream stream given a JavaScript value e, perform ! 799 // WritableStreamDefaultControllerErrorIfNeeded(stream.[[controller]], e). 800 void WritableStream::ErrorNative(JSContext* aCx, JS::Handle<JS::Value> aError, 801 ErrorResult& aRv) { 802 // MOZ_KnownLive here instead of MOZ_KNOWN_LIVE at the field, because 803 // mController is set outside of the constructor 804 WritableStreamDefaultControllerErrorIfNeeded(aCx, MOZ_KnownLive(mController), 805 aError, aRv); 806 } 807 808 } // namespace mozilla::dom