WritableStreamDefaultController.cpp (23060B)
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/WritableStreamDefaultController.h" 8 9 #include "js/Exception.h" 10 #include "js/TypeDecls.h" 11 #include "js/Value.h" 12 #include "mozilla/AlreadyAddRefed.h" 13 #include "mozilla/Attributes.h" 14 #include "mozilla/dom/AbortSignal.h" 15 #include "mozilla/dom/Promise-inl.h" 16 #include "mozilla/dom/Promise.h" 17 #include "mozilla/dom/UnderlyingSinkBinding.h" 18 #include "mozilla/dom/WritableStream.h" 19 #include "mozilla/dom/WritableStreamDefaultControllerBinding.h" 20 #include "nsCycleCollectionParticipant.h" 21 #include "nsDebug.h" 22 #include "nsISupports.h" 23 24 namespace mozilla::dom { 25 26 using namespace streams_abstract; 27 28 // Note: Using the individual macros vs NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE 29 // because I need to specificy a manual implementation of 30 // NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN. 31 NS_IMPL_CYCLE_COLLECTION_CLASS(WritableStreamDefaultController) 32 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WritableStreamDefaultController) 33 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mSignal, mStrategySizeAlgorithm, 34 mAlgorithms, mStream) 35 tmp->mQueue.clear(); 36 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 37 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 38 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WritableStreamDefaultController) 39 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mSignal, mStrategySizeAlgorithm, 40 mAlgorithms, mStream) 41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 42 43 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WritableStreamDefaultController) 44 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER 45 // Trace the associated queue. 46 for (const auto& queueEntry : tmp->mQueue) { 47 aCallbacks.Trace(&queueEntry->mValue, "mQueue.mValue", aClosure); 48 } 49 NS_IMPL_CYCLE_COLLECTION_TRACE_END 50 51 NS_IMPL_CYCLE_COLLECTING_ADDREF(WritableStreamDefaultController) 52 NS_IMPL_CYCLE_COLLECTING_RELEASE(WritableStreamDefaultController) 53 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableStreamDefaultController) 54 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 55 NS_INTERFACE_MAP_ENTRY(nsISupports) 56 NS_INTERFACE_MAP_END 57 58 WritableStreamDefaultController::WritableStreamDefaultController( 59 nsISupports* aGlobal, WritableStream& aStream) 60 : mGlobal(do_QueryInterface(aGlobal)), mStream(&aStream) { 61 mozilla::HoldJSObjects(this); 62 } 63 64 WritableStreamDefaultController::~WritableStreamDefaultController() { 65 // MG:XXX: LinkedLists are required to be empty at destruction, but it seems 66 // it is possible to have a controller be destructed while still 67 // having entries in its queue. 68 // 69 // This needs to be verified as not indicating some other issue. 70 mQueue.clear(); 71 mozilla::DropJSObjects(this); 72 } 73 74 JSObject* WritableStreamDefaultController::WrapObject( 75 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 76 return WritableStreamDefaultController_Binding::Wrap(aCx, this, aGivenProto); 77 } 78 79 // https://streams.spec.whatwg.org/#ws-default-controller-error 80 void WritableStreamDefaultController::Error(JSContext* aCx, 81 JS::Handle<JS::Value> aError, 82 ErrorResult& aRv) { 83 // Step 1. Let state be this.[[stream]].[[state]]. 84 // Step 2. If state is not "writable", return. 85 if (mStream->State() != WritableStream::WriterState::Writable) { 86 return; 87 } 88 // Step 3. Perform ! WritableStreamDefaultControllerError(this, e). 89 RefPtr<WritableStreamDefaultController> thisRefPtr = this; 90 WritableStreamDefaultControllerError(aCx, thisRefPtr, aError, aRv); 91 } 92 93 // https://streams.spec.whatwg.org/#ws-default-controller-private-abort 94 already_AddRefed<Promise> WritableStreamDefaultController::AbortSteps( 95 JSContext* aCx, JS::Handle<JS::Value> aReason, ErrorResult& aRv) { 96 // Step 1. Let result be the result of performing this.[[abortAlgorithm]], 97 // passing reason. 98 RefPtr<UnderlyingSinkAlgorithmsBase> algorithms = mAlgorithms; 99 Optional<JS::Handle<JS::Value>> optionalReason(aCx, aReason); 100 RefPtr<Promise> abortPromise = 101 algorithms->AbortCallback(aCx, optionalReason, aRv); 102 if (aRv.Failed()) { 103 return nullptr; 104 } 105 106 // Step 2. Perform ! WritableStreamDefaultControllerClearAlgorithms(this). 107 ClearAlgorithms(); 108 109 // Step 3. Return result. 110 return abortPromise.forget(); 111 } 112 113 // https://streams.spec.whatwg.org/#ws-default-controller-private-error 114 void WritableStreamDefaultController::ErrorSteps() { 115 // Step 1. Perform ! ResetQueue(this). 116 ResetQueue(this); 117 } 118 119 void WritableStreamDefaultController::SetSignal(AbortSignal* aSignal) { 120 MOZ_ASSERT(aSignal); 121 mSignal = aSignal; 122 } 123 124 namespace streams_abstract { 125 126 MOZ_CAN_RUN_SCRIPT static void 127 WritableStreamDefaultControllerAdvanceQueueIfNeeded( 128 JSContext* aCx, WritableStreamDefaultController* aController, 129 ErrorResult& aRv); 130 131 // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller 132 void SetUpWritableStreamDefaultController( 133 JSContext* aCx, WritableStream* aStream, 134 WritableStreamDefaultController* aController, 135 UnderlyingSinkAlgorithmsBase* aAlgorithms, double aHighWaterMark, 136 QueuingStrategySize* aSizeAlgorithm, ErrorResult& aRv) { 137 // Step 1. Assert: stream implements WritableStream. 138 // Step 2. Assert: stream.[[controller]] is undefined. 139 MOZ_ASSERT(!aStream->Controller()); 140 141 // Step 3. Set controller.[[stream]] to stream. 142 // Note: Already set in 143 // SetUpWritableStreamDefaultControllerFromUnderlyingSink. 144 MOZ_ASSERT(aController->Stream() == aStream); 145 146 // Step 4. Set stream.[[controller]] to controller. 147 aStream->SetController(*aController); 148 149 // Step 5. Perform ! ResetQueue(controller). 150 ResetQueue(aController); 151 152 // Step 6. Set controller.[[signal]] to a new AbortSignal. 153 RefPtr<AbortSignal> signal = 154 AbortSignal::Create(aController->GetParentObject(), SignalAborted::No, 155 JS::UndefinedHandleValue); 156 157 aController->SetSignal(signal); 158 159 // Step 7. Set controller.[[started]] to false. 160 aController->SetStarted(false); 161 162 // Step 8. Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm. 163 aController->SetStrategySizeAlgorithm(aSizeAlgorithm); 164 165 // Step 9. Set controller.[[strategyHWM]] to highWaterMark. 166 aController->SetStrategyHWM(aHighWaterMark); 167 168 // Step 10. Set controller.[[writeAlgorithm]] to writeAlgorithm. 169 // Step 11. Set controller.[[closeAlgorithm]] to closeAlgorithm. 170 // Step 12. Set controller.[[abortAlgorithm]] to abortAlgorithm. 171 aController->SetAlgorithms(*aAlgorithms); 172 173 // Step 13. Let backpressure be ! 174 // WritableStreamDefaultControllerGetBackpressure(controller). 175 bool backpressure = aController->GetBackpressure(); 176 177 // Step 14. Perform ! WritableStreamUpdateBackpressure(stream, backpressure). 178 aStream->UpdateBackpressure(backpressure); 179 180 // Step 15. Let startResult be the result of performing startAlgorithm. (This 181 // may throw an exception.) 182 JS::Rooted<JS::Value> startResult(aCx, JS::UndefinedValue()); 183 RefPtr<WritableStreamDefaultController> controller(aController); 184 aAlgorithms->StartCallback(aCx, *controller, &startResult, aRv); 185 if (aRv.Failed()) { 186 return; 187 } 188 189 // Step 16. Let startPromise be a promise resolved with startResult. 190 RefPtr<Promise> startPromise = 191 Promise::CreateInfallible(aStream->GetParentObject()); 192 startPromise->MaybeResolve(startResult); 193 194 // Step 17/18. 195 startPromise->AddCallbacksWithCycleCollectedArgs( 196 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv, 197 WritableStreamDefaultController* aController) 198 MOZ_CAN_RUN_SCRIPT_BOUNDARY { 199 // Step 17. Upon fulfillment of startPromise, 200 // Step 17.1. Assert: stream.[[state]] is "writable" or "erroring". 201 MOZ_ASSERT(aController->Stream()->State() == 202 WritableStream::WriterState::Writable || 203 aController->Stream()->State() == 204 WritableStream::WriterState::Erroring); 205 // Step 17.2. Set controller.[[started]] to true. 206 aController->SetStarted(true); 207 // Step 17.3 Perform 208 // !WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller). 209 WritableStreamDefaultControllerAdvanceQueueIfNeeded( 210 aCx, MOZ_KnownLive(aController), aRv); 211 }, 212 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv, 213 WritableStreamDefaultController* aController) 214 MOZ_CAN_RUN_SCRIPT_BOUNDARY { 215 RefPtr<WritableStream> stream = aController->Stream(); 216 // Step 18. Upon rejection of startPromise with reason r, 217 // Step 18.1. Assert: stream.[[state]] is "writable" or "erroring". 218 MOZ_ASSERT( 219 stream->State() == WritableStream::WriterState::Writable || 220 stream->State() == WritableStream::WriterState::Erroring); 221 // Step 18.2. Set controller.[[started]] to true. 222 aController->SetStarted(true); 223 // Step 18.3. Perform ! WritableStreamDealWithRejection(stream, r). 224 stream->DealWithRejection(aCx, aValue, aRv); 225 }, 226 RefPtr(aController)); 227 } 228 229 // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink 230 void SetUpWritableStreamDefaultControllerFromUnderlyingSink( 231 JSContext* aCx, WritableStream* aStream, 232 JS::Handle<JSObject*> aUnderlyingSink, UnderlyingSink& aUnderlyingSinkDict, 233 double aHighWaterMark, QueuingStrategySize* aSizeAlgorithm, 234 ErrorResult& aRv) { 235 // Step 1. 236 RefPtr<WritableStreamDefaultController> controller = 237 new WritableStreamDefaultController(aStream->GetParentObject(), *aStream); 238 239 // Step 2 - 9. 240 auto algorithms = MakeRefPtr<UnderlyingSinkAlgorithms>( 241 aStream->GetParentObject(), aUnderlyingSink, aUnderlyingSinkDict); 242 243 // Step 10. 244 SetUpWritableStreamDefaultController(aCx, aStream, controller, algorithms, 245 aHighWaterMark, aSizeAlgorithm, aRv); 246 } 247 248 // https://streams.spec.whatwg.org/#writable-stream-default-controller-process-close 249 MOZ_CAN_RUN_SCRIPT static void WritableStreamDefaultControllerProcessClose( 250 JSContext* aCx, WritableStreamDefaultController* aController, 251 ErrorResult& aRv) { 252 // Step 1. Let stream be controller.[[stream]]. 253 RefPtr<WritableStream> stream = aController->Stream(); 254 255 // Step 2. Perform ! WritableStreamMarkCloseRequestInFlight(stream). 256 stream->MarkCloseRequestInFlight(); 257 258 // Step 3. Perform ! DequeueValue(controller). 259 JS::Rooted<JS::Value> value(aCx); 260 DequeueValue(aController, &value); 261 262 // Step 4. Assert: controller.[[queue]] is empty. 263 MOZ_ASSERT(aController->Queue().isEmpty()); 264 265 // Step 5. Let sinkClosePromise be the result of performing 266 // controller.[[closeAlgorithm]]. 267 RefPtr<UnderlyingSinkAlgorithmsBase> algorithms = 268 aController->GetAlgorithms(); 269 RefPtr<Promise> sinkClosePromise = algorithms->CloseCallback(aCx, aRv); 270 if (aRv.Failed()) { 271 return; 272 } 273 274 // Step 6. Perform ! 275 // WritableStreamDefaultControllerClearAlgorithms(controller). 276 aController->ClearAlgorithms(); 277 278 // Step 7 + 8. 279 sinkClosePromise->AddCallbacksWithCycleCollectedArgs( 280 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv, 281 WritableStreamDefaultController* aController) { 282 RefPtr<WritableStream> stream = aController->Stream(); 283 // Step 7. Upon fulfillment of sinkClosePromise, 284 // Step 7.1. Perform ! WritableStreamFinishInFlightClose(stream). 285 stream->FinishInFlightClose(); 286 }, 287 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv, 288 WritableStreamDefaultController* aController) 289 MOZ_CAN_RUN_SCRIPT_BOUNDARY { 290 RefPtr<WritableStream> stream = aController->Stream(); 291 // Step 8. Upon rejection of sinkClosePromise with reason reason, 292 // Step 8.1. Perform 293 // ! WritableStreamFinishInFlightCloseWithError(stream, reason). 294 stream->FinishInFlightCloseWithError(aCx, aValue, aRv); 295 }, 296 RefPtr(aController)); 297 } 298 299 // https://streams.spec.whatwg.org/#writable-stream-default-controller-process-write 300 MOZ_CAN_RUN_SCRIPT static void WritableStreamDefaultControllerProcessWrite( 301 JSContext* aCx, WritableStreamDefaultController* aController, 302 JS::Handle<JS::Value> aChunk, ErrorResult& aRv) { 303 // Step 1. Let stream be controller.[[stream]]. 304 RefPtr<WritableStream> stream = aController->Stream(); 305 306 // Step 2. Perform ! WritableStreamMarkFirstWriteRequestInFlight(stream). 307 stream->MarkFirstWriteRequestInFlight(); 308 309 // Step 3. Let sinkWritePromise be the result of performing 310 // controller.[[writeAlgorithm]], passing in chunk. 311 RefPtr<UnderlyingSinkAlgorithmsBase> algorithms = 312 aController->GetAlgorithms(); 313 RefPtr<Promise> sinkWritePromise = 314 algorithms->WriteCallback(aCx, aChunk, *aController, aRv); 315 if (aRv.Failed()) { 316 return; 317 } 318 319 // Step 4 + 5: 320 sinkWritePromise->AddCallbacksWithCycleCollectedArgs( 321 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv, 322 WritableStreamDefaultController* aController) 323 MOZ_CAN_RUN_SCRIPT_BOUNDARY { 324 RefPtr<WritableStream> stream = aController->Stream(); 325 326 // Step 4.1. Perform ! WritableStreamFinishInFlightWrite(stream). 327 stream->FinishInFlightWrite(); 328 329 // Step 4.2. Let state be stream.[[state]]. 330 WritableStream::WriterState state = stream->State(); 331 332 // Step 4.3. Assert: state is "writable" or "erroring". 333 MOZ_ASSERT(state == WritableStream::WriterState::Writable || 334 state == WritableStream::WriterState::Erroring); 335 336 // Step 4.4. Perform ! DequeueValue(controller). 337 JS::Rooted<JS::Value> value(aCx); 338 DequeueValue(aController, &value); 339 340 // Step 4.5. If ! WritableStreamCloseQueuedOrInFlight(stream) is 341 // false and state is "writable", 342 if (!stream->CloseQueuedOrInFlight() && 343 state == WritableStream::WriterState::Writable) { 344 // Step 4.5.1. Let backpressure be ! 345 // WritableStreamDefaultControllerGetBackpressure(controller). 346 bool backpressure = aController->GetBackpressure(); 347 // Step 4.5.2. Perform ! WritableStreamUpdateBackpressure(stream, 348 // backpressure). 349 stream->UpdateBackpressure(backpressure); 350 } 351 352 // Step 4.6. Perform ! 353 // WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller). 354 WritableStreamDefaultControllerAdvanceQueueIfNeeded( 355 aCx, MOZ_KnownLive(aController), aRv); 356 }, 357 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv, 358 WritableStreamDefaultController* aController) 359 MOZ_CAN_RUN_SCRIPT_BOUNDARY { 360 RefPtr<WritableStream> stream = aController->Stream(); 361 362 // Step 5.1. If stream.[[state]] is "writable", perform ! 363 // WritableStreamDefaultControllerClearAlgorithms(controller). 364 if (stream->State() == WritableStream::WriterState::Writable) { 365 aController->ClearAlgorithms(); 366 } 367 368 // Step 5.2. Perform ! 369 // WritableStreamFinishInFlightWriteWithError(stream, reason) 370 stream->FinishInFlightWriteWithError(aCx, aValue, aRv); 371 }, 372 RefPtr(aController)); 373 } 374 375 // We use a JS::MagicValue to represent the close sentinel required by the spec. 376 // Normal JavaScript code can not generate magic values, so we can use this 377 // as a special value. However care has to be taken to not leak the magic value 378 // to other code. 379 constexpr JSWhyMagic CLOSE_SENTINEL = JS_GENERIC_MAGIC; 380 381 // https://streams.spec.whatwg.org/#writable-stream-default-controller-advance-queue-if-needed 382 static void WritableStreamDefaultControllerAdvanceQueueIfNeeded( 383 JSContext* aCx, WritableStreamDefaultController* aController, 384 ErrorResult& aRv) { 385 // Step 1. Let stream be controller.[[stream]]. 386 RefPtr<WritableStream> stream = aController->Stream(); 387 388 // Step 2. If controller.[[started]] is false, return. 389 if (!aController->Started()) { 390 return; 391 } 392 393 // Step 3. If stream.[[inFlightWriteRequest]] is not undefined, return. 394 if (stream->GetInFlightWriteRequest()) { 395 return; 396 } 397 398 // Step 4. Let state be stream.[[state]]. 399 WritableStream::WriterState state = stream->State(); 400 401 // Step 5. Assert: state is not "closed" or "errored". 402 MOZ_ASSERT(state != WritableStream::WriterState::Closed && 403 state != WritableStream::WriterState::Errored); 404 405 // Step 6. If state is "erroring", 406 if (state == WritableStream::WriterState::Erroring) { 407 // Step 6.1. Perform ! WritableStreamFinishErroring(stream). 408 stream->FinishErroring(aCx, aRv); 409 410 // Step 6.2. Return. 411 return; 412 } 413 414 // Step 7. If controller.[[queue]] is empty, return. 415 if (aController->Queue().isEmpty()) { 416 return; 417 } 418 419 // Step 8. Let value be ! PeekQueueValue(controller). 420 JS::Rooted<JS::Value> value(aCx); 421 PeekQueueValue(aController, &value); 422 423 // Step 9. If value is the close sentinel, perform ! 424 // WritableStreamDefaultControllerProcessClose(controller). 425 if (value.isMagic(CLOSE_SENTINEL)) { 426 WritableStreamDefaultControllerProcessClose(aCx, aController, aRv); 427 return; 428 } 429 430 // Step 10. Otherwise, perform ! 431 // WritableStreamDefaultControllerProcessWrite(controller, value). 432 WritableStreamDefaultControllerProcessWrite(aCx, aController, value, aRv); 433 } 434 435 // https://streams.spec.whatwg.org/#writable-stream-default-controller-close 436 void WritableStreamDefaultControllerClose( 437 JSContext* aCx, WritableStreamDefaultController* aController, 438 ErrorResult& aRv) { 439 // Step 1. Perform ! EnqueueValueWithSize(controller, close sentinel, 0). 440 JS::Rooted<JS::Value> aCloseSentinel(aCx, JS::MagicValue(CLOSE_SENTINEL)); 441 EnqueueValueWithSize(aController, aCloseSentinel, 0, aRv); 442 MOZ_ASSERT(!aRv.Failed()); 443 444 // Step 2. Perform ! 445 // WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller). 446 WritableStreamDefaultControllerAdvanceQueueIfNeeded(aCx, aController, aRv); 447 } 448 449 // https://streams.spec.whatwg.org/#writable-stream-default-controller-write 450 void WritableStreamDefaultControllerWrite( 451 JSContext* aCx, WritableStreamDefaultController* aController, 452 JS::Handle<JS::Value> aChunk, double chunkSize, ErrorResult& aRv) { 453 // Step 1. Let enqueueResult be EnqueueValueWithSize(controller, chunk, 454 // chunkSize). 455 IgnoredErrorResult rv; 456 EnqueueValueWithSize(aController, aChunk, chunkSize, rv); 457 458 // Step 2. If enqueueResult is an abrupt completion, 459 if (rv.MaybeSetPendingException(aCx, 460 "WritableStreamDefaultController.write")) { 461 JS::Rooted<JS::Value> error(aCx); 462 JS_GetPendingException(aCx, &error); 463 JS_ClearPendingException(aCx); 464 465 // Step 2.1. Perform ! 466 // WritableStreamDefaultControllerErrorIfNeeded(controller, 467 // enqueueResult.[[Value]]). 468 WritableStreamDefaultControllerErrorIfNeeded(aCx, aController, error, aRv); 469 470 // Step 2.2. Return. 471 return; 472 } 473 474 // Step 3. Let stream be controller.[[stream]]. 475 RefPtr<WritableStream> stream = aController->Stream(); 476 477 // Step 4. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and 478 // stream.[[state]] is "writable", 479 if (!stream->CloseQueuedOrInFlight() && 480 stream->State() == WritableStream::WriterState::Writable) { 481 // Step 4.1. Let backpressure be 482 // !WritableStreamDefaultControllerGetBackpressure(controller). 483 bool backpressure = aController->GetBackpressure(); 484 485 // Step 4.2. Perform ! WritableStreamUpdateBackpressure(stream, 486 // backpressure). 487 stream->UpdateBackpressure(backpressure); 488 } 489 490 // Step 5. Perform 491 // ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller). 492 WritableStreamDefaultControllerAdvanceQueueIfNeeded(aCx, aController, aRv); 493 } 494 495 void WritableStreamDefaultControllerError( 496 JSContext* aCx, WritableStreamDefaultController* aController, 497 JS::Handle<JS::Value> aError, ErrorResult& aRv) { 498 // Step 1. Let stream be controller.[[stream]]. 499 RefPtr<WritableStream> stream = aController->Stream(); 500 501 // Step 2. Assert: stream.[[state]] is "writable". 502 MOZ_ASSERT(stream->State() == WritableStream::WriterState::Writable); 503 504 // Step 3. Perform 505 // ! WritableStreamDefaultControllerClearAlgorithms(controller). 506 aController->ClearAlgorithms(); 507 508 // Step 4.Perform ! WritableStreamStartErroring(stream, error). 509 stream->StartErroring(aCx, aError, aRv); 510 } 511 512 // https://streams.spec.whatwg.org/#writable-stream-default-controller-error-if-needed 513 void WritableStreamDefaultControllerErrorIfNeeded( 514 JSContext* aCx, WritableStreamDefaultController* aController, 515 JS::Handle<JS::Value> aError, ErrorResult& aRv) { 516 // Step 1. If controller.[[stream]].[[state]] is "writable", perform 517 // !WritableStreamDefaultControllerError(controller, error). 518 if (aController->Stream()->State() == WritableStream::WriterState::Writable) { 519 WritableStreamDefaultControllerError(aCx, aController, aError, aRv); 520 } 521 } 522 523 // https://streams.spec.whatwg.org/#writable-stream-default-controller-get-chunk-size 524 double WritableStreamDefaultControllerGetChunkSize( 525 JSContext* aCx, WritableStreamDefaultController* aController, 526 JS::Handle<JS::Value> aChunk, ErrorResult& aRv) { 527 // Step 1. Let returnValue be the result of performing 528 // controller.[[strategySizeAlgorithm]], passing in chunk, and interpreting 529 // the result as a completion record. 530 RefPtr<QueuingStrategySize> sizeAlgorithm( 531 aController->StrategySizeAlgorithm()); 532 533 // If !sizeAlgorithm, we return 1, which is inlined from 534 // https://streams.spec.whatwg.org/#make-size-algorithm-from-size-function 535 Optional<JS::Handle<JS::Value>> optionalChunk(aCx, aChunk); 536 537 double chunkSize = 538 sizeAlgorithm 539 ? sizeAlgorithm->Call( 540 optionalChunk, aRv, 541 "WritableStreamDefaultController.[[strategySizeAlgorithm]]", 542 CallbackObject::eRethrowExceptions) 543 : 1.0; 544 545 // Step 2. If returnValue is an abrupt completion, 546 if (aRv.MaybeSetPendingException( 547 aCx, "WritableStreamDefaultController.[[strategySizeAlgorithm]]")) { 548 JS::Rooted<JS::Value> error(aCx); 549 JS_GetPendingException(aCx, &error); 550 JS_ClearPendingException(aCx); 551 552 // Step 2.1. Perform ! 553 // WritableStreamDefaultControllerErrorIfNeeded(controller, 554 // returnValue.[[Value]]). 555 WritableStreamDefaultControllerErrorIfNeeded(aCx, aController, error, aRv); 556 557 // Step 2.2. Return 1. 558 return 1.0; 559 } 560 561 // Step 3. Return returnValue.[[Value]]. 562 return chunkSize; 563 } 564 565 } // namespace streams_abstract 566 567 } // namespace mozilla::dom