TransformStream.cpp (28799B)
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/TransformStream.h" 8 9 #include "StreamUtils.h" 10 #include "TransformerCallbackHelpers.h" 11 #include "UnderlyingSourceCallbackHelpers.h" 12 #include "js/TypeDecls.h" 13 #include "mozilla/Attributes.h" 14 #include "mozilla/dom/Promise-inl.h" 15 #include "mozilla/dom/Promise.h" 16 #include "mozilla/dom/ReadableStream.h" 17 #include "mozilla/dom/RootedDictionary.h" 18 #include "mozilla/dom/TransformStreamBinding.h" 19 #include "mozilla/dom/TransformerBinding.h" 20 #include "mozilla/dom/WritableStream.h" 21 #include "nsWrapperCache.h" 22 23 namespace mozilla::dom { 24 25 using namespace streams_abstract; 26 27 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TransformStream, mGlobal, 28 mBackpressureChangePromise, mController, 29 mReadable, mWritable) 30 NS_IMPL_CYCLE_COLLECTING_ADDREF(TransformStream) 31 NS_IMPL_CYCLE_COLLECTING_RELEASE(TransformStream) 32 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStream) 33 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 34 NS_INTERFACE_MAP_ENTRY(nsISupports) 35 NS_INTERFACE_MAP_END 36 37 // https://streams.spec.whatwg.org/#transformstream-set-up 38 // (except this instead creates a new TransformStream rather than accepting an 39 // existing instance) 40 already_AddRefed<TransformStream> TransformStream::CreateGeneric( 41 const GlobalObject& aGlobal, TransformerAlgorithmsWrapper& aAlgorithms, 42 ErrorResult& aRv) { 43 // Step 1. Let writableHighWaterMark be 1. 44 double writableHighWaterMark = 1; 45 46 // Step 2. Let writableSizeAlgorithm be an algorithm that returns 1. 47 // Note: Callers should recognize nullptr as a callback that returns 1. See 48 // also WritableStream::Constructor for this design decision. 49 RefPtr<QueuingStrategySize> writableSizeAlgorithm; 50 51 // Step 3. Let readableHighWaterMark be 0. 52 double readableHighWaterMark = 0; 53 54 // Step 4. Let readableSizeAlgorithm be an algorithm that returns 1. 55 // Note: Callers should recognize nullptr as a callback that returns 1. See 56 // also ReadableStream::Constructor for this design decision. 57 RefPtr<QueuingStrategySize> readableSizeAlgorithm; 58 59 // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps 60 // given a value chunk: 61 // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps: 62 // (Done by TransformerAlgorithmsWrapper) 63 64 // Step 7. Let startPromise be a promise resolved with undefined. 65 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 66 RefPtr<Promise> startPromise = 67 Promise::CreateResolvedWithUndefined(global, aRv); 68 if (!startPromise) { 69 return nullptr; 70 } 71 72 // Step 8. Perform ! InitializeTransformStream(stream, startPromise, 73 // writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark, 74 // readableSizeAlgorithm). 75 RefPtr<TransformStream> stream = 76 new TransformStream(global, nullptr, nullptr); 77 stream->Initialize(aGlobal.Context(), startPromise, writableHighWaterMark, 78 writableSizeAlgorithm, readableHighWaterMark, 79 readableSizeAlgorithm, aRv); 80 if (aRv.Failed()) { 81 return nullptr; 82 } 83 84 // Step 9. Let controller be a new TransformStreamDefaultController. 85 auto controller = MakeRefPtr<TransformStreamDefaultController>(global); 86 87 // Step 10. Perform ! SetUpTransformStreamDefaultController(stream, 88 // controller, transformAlgorithmWrapper, flushAlgorithmWrapper). 89 SetUpTransformStreamDefaultController(aGlobal.Context(), *stream, *controller, 90 aAlgorithms); 91 92 return stream.forget(); 93 } 94 95 TransformStream::TransformStream(nsIGlobalObject* aGlobal) : mGlobal(aGlobal) { 96 mozilla::HoldJSObjects(this); 97 } 98 99 TransformStream::TransformStream(nsIGlobalObject* aGlobal, 100 ReadableStream* aReadable, 101 WritableStream* aWritable) 102 : mGlobal(aGlobal), mReadable(aReadable), mWritable(aWritable) { 103 mozilla::HoldJSObjects(this); 104 } 105 106 TransformStream::~TransformStream() { mozilla::DropJSObjects(this); } 107 108 JSObject* TransformStream::WrapObject(JSContext* aCx, 109 JS::Handle<JSObject*> aGivenProto) { 110 return TransformStream_Binding::Wrap(aCx, this, aGivenProto); 111 } 112 113 namespace streams_abstract { 114 115 // https://streams.spec.whatwg.org/#transform-stream-error-writable-and-unblock-write 116 void TransformStreamErrorWritableAndUnblockWrite(JSContext* aCx, 117 TransformStream* aStream, 118 JS::Handle<JS::Value> aError, 119 ErrorResult& aRv) { 120 // Step 1: Perform ! 121 // TransformStreamDefaultControllerClearAlgorithms(stream.[[controller]]). 122 aStream->Controller()->SetAlgorithms(nullptr); 123 124 // Step 2: Perform ! 125 // WritableStreamDefaultControllerErrorIfNeeded(stream.[[writable]].[[controller]], 126 // e). 127 // TODO: Remove MOZ_KnownLive (bug 1761577) 128 WritableStreamDefaultControllerErrorIfNeeded( 129 aCx, MOZ_KnownLive(aStream->Writable()->Controller()), aError, aRv); 130 if (aRv.Failed()) { 131 return; 132 } 133 134 // Step 3: If stream.[[backpressure]] is true, perform ! 135 // TransformStreamSetBackpressure(stream, false). 136 if (aStream->Backpressure()) { 137 aStream->SetBackpressure(false); 138 } 139 } 140 141 // https://streams.spec.whatwg.org/#transform-stream-error 142 void TransformStreamError(JSContext* aCx, TransformStream* aStream, 143 JS::Handle<JS::Value> aError, ErrorResult& aRv) { 144 // Step 1: Perform ! 145 // ReadableStreamDefaultControllerError(stream.[[readable]].[[controller]], 146 // e). 147 ReadableStreamDefaultControllerError( 148 aCx, aStream->Readable()->Controller()->AsDefault(), aError, aRv); 149 if (aRv.Failed()) { 150 return; 151 } 152 153 // Step 2: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, e). 154 TransformStreamErrorWritableAndUnblockWrite(aCx, aStream, aError, aRv); 155 } 156 157 } // namespace streams_abstract 158 159 // https://streams.spec.whatwg.org/#transform-stream-default-controller-perform-transform 160 MOZ_CAN_RUN_SCRIPT static already_AddRefed<Promise> 161 TransformStreamDefaultControllerPerformTransform( 162 JSContext* aCx, TransformStreamDefaultController* aController, 163 JS::Handle<JS::Value> aChunk, ErrorResult& aRv) { 164 // Step 1: Let transformPromise be the result of performing 165 // controller.[[transformAlgorithm]], passing chunk. 166 RefPtr<TransformerAlgorithmsBase> algorithms = aController->Algorithms(); 167 RefPtr<Promise> transformPromise = 168 algorithms->TransformCallback(aCx, aChunk, *aController, aRv); 169 if (aRv.Failed()) { 170 return nullptr; 171 } 172 173 // Step 2: Return the result of reacting to transformPromise with the 174 // following rejection steps given the argument r: 175 auto result = transformPromise->CatchWithCycleCollectedArgs( 176 [](JSContext* aCx, JS::Handle<JS::Value> aError, ErrorResult& aRv, 177 const RefPtr<TransformStreamDefaultController>& aController) 178 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA -> already_AddRefed<Promise> { 179 // Step 2.1: Perform ! TransformStreamError(controller.[[stream]], 180 // r). 181 // TODO: Remove MOZ_KnownLive (bug 1761577) 182 TransformStreamError(aCx, MOZ_KnownLive(aController->Stream()), 183 aError, aRv); 184 if (aRv.Failed()) { 185 return nullptr; 186 } 187 188 // Step 2.2: Throw r. 189 JS::Rooted<JS::Value> r(aCx, aError); 190 aRv.MightThrowJSException(); 191 aRv.ThrowJSException(aCx, r); 192 return nullptr; 193 }, 194 RefPtr(aController)); 195 if (result.isErr()) { 196 aRv.Throw(result.unwrapErr()); 197 return nullptr; 198 } 199 return result.unwrap().forget(); 200 } 201 202 // https://streams.spec.whatwg.org/#initialize-transform-stream 203 class TransformStreamUnderlyingSinkAlgorithms final 204 : public UnderlyingSinkAlgorithmsBase { 205 public: 206 NS_DECL_ISUPPORTS_INHERITED 207 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED( 208 TransformStreamUnderlyingSinkAlgorithms, UnderlyingSinkAlgorithmsBase) 209 210 TransformStreamUnderlyingSinkAlgorithms(Promise* aStartPromise, 211 TransformStream* aStream) 212 : mStartPromise(aStartPromise), mStream(aStream) {} 213 214 void StartCallback(JSContext* aCx, 215 WritableStreamDefaultController& aController, 216 JS::MutableHandle<JS::Value> aRetVal, 217 ErrorResult& aRv) override { 218 // Step 1. Let startAlgorithm be an algorithm that returns startPromise. 219 // (Same as TransformStreamUnderlyingSourceAlgorithms::StartCallback) 220 aRetVal.setObject(*mStartPromise->PromiseObj()); 221 } 222 223 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> WriteCallback( 224 JSContext* aCx, JS::Handle<JS::Value> aChunk, 225 WritableStreamDefaultController& aController, ErrorResult& aRv) override { 226 // Step 2. Let writeAlgorithm be the following steps, taking a chunk 227 // argument: 228 // Step 2.1. Return ! TransformStreamDefaultSinkWriteAlgorithm(stream, 229 // chunk). 230 231 // Inlining TransformStreamDefaultSinkWriteAlgorithm here: 232 // https://streams.spec.whatwg.org/#transform-stream-default-sink-write-algorithm 233 234 // Step 1: Assert: stream.[[writable]].[[state]] is "writable". 235 MOZ_ASSERT(mStream->Writable()->State() == 236 WritableStream::WriterState::Writable); 237 238 // Step 2: Let controller be stream.[[controller]]. 239 RefPtr<TransformStreamDefaultController> controller = mStream->Controller(); 240 241 // Step 3: If stream.[[backpressure]] is true, 242 if (mStream->Backpressure()) { 243 // Step 3.1: Let backpressureChangePromise be 244 // stream.[[backpressureChangePromise]]. 245 RefPtr<Promise> backpressureChangePromise = 246 mStream->BackpressureChangePromise(); 247 248 // Step 3.2: Assert: backpressureChangePromise is not undefined. 249 MOZ_ASSERT(backpressureChangePromise); 250 251 // Step 3.3: Return the result of reacting to backpressureChangePromise 252 // with the following fulfillment steps: 253 auto result = backpressureChangePromise->ThenWithCycleCollectedArgsJS( 254 [](JSContext* aCx, JS::Handle<JS::Value>, ErrorResult& aRv, 255 const RefPtr<TransformStream>& aStream, 256 const RefPtr<TransformStreamDefaultController>& aController, 257 JS::Handle<JS::Value> aChunk) 258 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA -> already_AddRefed<Promise> { 259 // Step 1: Let writable be stream.[[writable]]. 260 RefPtr<WritableStream> writable = aStream->Writable(); 261 262 // Step 2: Let state be writable.[[state]]. 263 WritableStream::WriterState state = writable->State(); 264 265 // Step 3: If state is "erroring", throw 266 // writable.[[storedError]]. 267 if (state == WritableStream::WriterState::Erroring) { 268 JS::Rooted<JS::Value> storedError(aCx, 269 writable->StoredError()); 270 aRv.MightThrowJSException(); 271 aRv.ThrowJSException(aCx, storedError); 272 return nullptr; 273 } 274 275 // Step 4: Assert: state is "writable". 276 MOZ_ASSERT(state == WritableStream::WriterState::Writable); 277 278 // Step 5: Return ! 279 // TransformStreamDefaultControllerPerformTransform(controller, 280 // chunk). 281 return TransformStreamDefaultControllerPerformTransform( 282 aCx, aController, aChunk, aRv); 283 }, 284 std::make_tuple(mStream, controller), std::make_tuple(aChunk)); 285 286 if (result.isErr()) { 287 aRv.Throw(result.unwrapErr()); 288 return nullptr; 289 } 290 return result.unwrap().forget(); 291 } 292 293 // Step 4: Return ! 294 // TransformStreamDefaultControllerPerformTransform(controller, chunk). 295 return TransformStreamDefaultControllerPerformTransform(aCx, controller, 296 aChunk, aRv); 297 } 298 299 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> AbortCallback( 300 JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason, 301 ErrorResult& aRv) override { 302 // Step 3. Let abortAlgorithm be the following steps, taking a reason 303 // argument: 304 // Step 3.1. Return ! TransformStreamDefaultSinkAbortAlgorithm(stream, 305 // reason). 306 307 // Inlining TransformStreamDefaultSinkAbortAlgorithm here: 308 // https://streams.spec.whatwg.org/#transform-stream-default-sink-abort-algorithm 309 310 // Step 1:Perform ! TransformStreamError(stream, reason). 311 TransformStreamError( 312 aCx, mStream, 313 aReason.WasPassed() ? aReason.Value() : JS::UndefinedHandleValue, aRv); 314 if (aRv.Failed()) { 315 return nullptr; 316 } 317 318 // Step 2: Return a promise resolved with undefined. 319 return Promise::CreateResolvedWithUndefined(mStream->GetParentObject(), 320 aRv); 321 } 322 323 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CloseCallback( 324 JSContext* aCx, ErrorResult& aRv) override { 325 // Step 4. Let closeAlgorithm be the following steps: 326 // Step 4.1. Return ! TransformStreamDefaultSinkCloseAlgorithm(stream). 327 328 // Inlining TransformStreamDefaultSinkCloseAlgorithm here: 329 // https://streams.spec.whatwg.org/#transform-stream-default-sink-close-algorithm 330 331 // Step 1: Let readable be stream.[[readable]]. 332 RefPtr<ReadableStream> readable = mStream->Readable(); 333 334 // Step 2: Let controller be stream.[[controller]]. 335 RefPtr<TransformStreamDefaultController> controller = mStream->Controller(); 336 337 // Step 3: Let flushPromise be the result of performing 338 // controller.[[flushAlgorithm]]. 339 RefPtr<TransformerAlgorithmsBase> algorithms = controller->Algorithms(); 340 RefPtr<Promise> flushPromise = 341 algorithms->FlushCallback(aCx, *controller, aRv); 342 if (aRv.Failed()) { 343 return nullptr; 344 } 345 346 // Step 4: Perform ! 347 // TransformStreamDefaultControllerClearAlgorithms(controller). 348 controller->SetAlgorithms(nullptr); 349 350 // Step 5: Return the result of reacting to flushPromise: 351 Result<RefPtr<Promise>, nsresult> result = 352 flushPromise->ThenCatchWithCycleCollectedArgs( 353 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv, 354 const RefPtr<ReadableStream>& aReadable, 355 const RefPtr<TransformStream>& aStream) 356 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA 357 -> already_AddRefed<Promise> { 358 // Step 5.1: If flushPromise was fulfilled, then: 359 360 // Step 5.1.1: If readable.[[state]] is "errored", throw 361 // readable.[[storedError]]. 362 if (aReadable->State() == 363 ReadableStream::ReaderState::Errored) { 364 JS::Rooted<JS::Value> storedError(aCx, 365 aReadable->StoredError()); 366 aRv.MightThrowJSException(); 367 aRv.ThrowJSException(aCx, storedError); 368 return nullptr; 369 } 370 371 // Step 5.1.2: Perform ! 372 // ReadableStreamDefaultControllerClose(readable.[[controller]]). 373 ReadableStreamDefaultControllerClose( 374 aCx, MOZ_KnownLive(aReadable->Controller()->AsDefault()), 375 aRv); 376 return nullptr; 377 }, 378 [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv, 379 const RefPtr<ReadableStream>& aReadable, 380 const RefPtr<TransformStream>& aStream) 381 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA 382 -> already_AddRefed<Promise> { 383 // Step 5.2: If flushPromise was rejected with reason r, then: 384 385 // Step 5.2.1: Perform ! TransformStreamError(stream, r). 386 TransformStreamError(aCx, aStream, aValue, aRv); 387 if (aRv.Failed()) { 388 return nullptr; 389 } 390 391 // Step 5.2.2: Throw readable.[[storedError]]. 392 JS::Rooted<JS::Value> storedError(aCx, 393 aReadable->StoredError()); 394 aRv.MightThrowJSException(); 395 aRv.ThrowJSException(aCx, storedError); 396 return nullptr; 397 }, 398 readable, mStream); 399 400 if (result.isErr()) { 401 aRv.Throw(result.unwrapErr()); 402 return nullptr; 403 } 404 return result.unwrap().forget(); 405 } 406 407 protected: 408 ~TransformStreamUnderlyingSinkAlgorithms() override = default; 409 410 private: 411 RefPtr<Promise> mStartPromise; 412 // MOZ_KNOWN_LIVE because it won't be reassigned 413 MOZ_KNOWN_LIVE RefPtr<TransformStream> mStream; 414 }; 415 416 NS_IMPL_CYCLE_COLLECTION_INHERITED(TransformStreamUnderlyingSinkAlgorithms, 417 UnderlyingSinkAlgorithmsBase, mStartPromise, 418 mStream) 419 NS_IMPL_ADDREF_INHERITED(TransformStreamUnderlyingSinkAlgorithms, 420 UnderlyingSinkAlgorithmsBase) 421 NS_IMPL_RELEASE_INHERITED(TransformStreamUnderlyingSinkAlgorithms, 422 UnderlyingSinkAlgorithmsBase) 423 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStreamUnderlyingSinkAlgorithms) 424 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSinkAlgorithmsBase) 425 426 // https://streams.spec.whatwg.org/#initialize-transform-stream 427 class TransformStreamUnderlyingSourceAlgorithms final 428 : public UnderlyingSourceAlgorithmsBase { 429 public: 430 NS_DECL_ISUPPORTS_INHERITED 431 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED( 432 TransformStreamUnderlyingSourceAlgorithms, UnderlyingSourceAlgorithmsBase) 433 434 TransformStreamUnderlyingSourceAlgorithms(Promise* aStartPromise, 435 TransformStream* aStream) 436 : mStartPromise(aStartPromise), mStream(aStream) {} 437 438 void StartCallback(JSContext* aCx, ReadableStreamControllerBase& aController, 439 JS::MutableHandle<JS::Value> aRetVal, 440 ErrorResult& aRv) override { 441 // Step 1. Let startAlgorithm be an algorithm that returns startPromise. 442 // (Same as TransformStreamUnderlyingSinkAlgorithms::StartCallback) 443 aRetVal.setObject(*mStartPromise->PromiseObj()); 444 } 445 446 already_AddRefed<Promise> PullCallback( 447 JSContext* aCx, ReadableStreamControllerBase& aController, 448 ErrorResult& aRv) override { 449 // Step 6. Let pullAlgorithm be the following steps: 450 // Step 6.1. Return ! TransformStreamDefaultSourcePullAlgorithm(stream). 451 452 // Inlining TransformStreamDefaultSourcePullAlgorithm here: 453 // https://streams.spec.whatwg.org/#transform-stream-default-source-pull-algorithm 454 455 // Step 1: Assert: stream.[[backpressure]] is true. 456 MOZ_ASSERT(mStream->Backpressure()); 457 458 // Step 2: Assert: stream.[[backpressureChangePromise]] is not undefined. 459 MOZ_ASSERT(mStream->BackpressureChangePromise()); 460 461 // Step 3: Perform ! TransformStreamSetBackpressure(stream, false). 462 mStream->SetBackpressure(false); 463 464 // Step 4: Return stream.[[backpressureChangePromise]]. 465 return do_AddRef(mStream->BackpressureChangePromise()); 466 } 467 468 MOZ_CAN_RUN_SCRIPT already_AddRefed<Promise> CancelCallback( 469 JSContext* aCx, const Optional<JS::Handle<JS::Value>>& aReason, 470 ErrorResult& aRv) override { 471 // Step 7. Let cancelAlgorithm be the following steps, taking a reason 472 // argument: 473 // Step 7.1. Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, 474 // reason). 475 TransformStreamErrorWritableAndUnblockWrite( 476 aCx, mStream, 477 aReason.WasPassed() ? aReason.Value() : JS::UndefinedHandleValue, aRv); 478 if (aRv.Failed()) { 479 return nullptr; 480 } 481 482 // Step 7.2. Return a promise resolved with undefined. 483 return Promise::CreateResolvedWithUndefined(mStream->GetParentObject(), 484 aRv); 485 } 486 487 protected: 488 ~TransformStreamUnderlyingSourceAlgorithms() override = default; 489 490 private: 491 RefPtr<Promise> mStartPromise; 492 // MOZ_KNOWNLIVE because it will never be reassigned 493 MOZ_KNOWN_LIVE RefPtr<TransformStream> mStream; 494 }; 495 496 NS_IMPL_CYCLE_COLLECTION_INHERITED(TransformStreamUnderlyingSourceAlgorithms, 497 UnderlyingSourceAlgorithmsBase, 498 mStartPromise, mStream) 499 NS_IMPL_ADDREF_INHERITED(TransformStreamUnderlyingSourceAlgorithms, 500 UnderlyingSourceAlgorithmsBase) 501 NS_IMPL_RELEASE_INHERITED(TransformStreamUnderlyingSourceAlgorithms, 502 UnderlyingSourceAlgorithmsBase) 503 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION( 504 TransformStreamUnderlyingSourceAlgorithms) 505 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceAlgorithmsBase) 506 507 // https://streams.spec.whatwg.org/#transform-stream-set-backpressure 508 void TransformStream::SetBackpressure(bool aBackpressure) { 509 // Step 1. Assert: stream.[[backpressure]] is not backpressure. 510 MOZ_ASSERT(Backpressure() != aBackpressure); 511 512 // Step 2. If stream.[[backpressureChangePromise]] is not undefined, resolve 513 // stream.[[backpressureChangePromise]] with undefined. 514 if (Promise* promise = BackpressureChangePromise()) { 515 promise->MaybeResolveWithUndefined(); 516 } 517 518 // Step 3. Set stream.[[backpressureChangePromise]] to a new promise. 519 RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject()); 520 mBackpressureChangePromise = promise; 521 522 // Step 4. Set stream.[[backpressure]] to backpressure. 523 mBackpressure = aBackpressure; 524 } 525 526 // https://streams.spec.whatwg.org/#initialize-transform-stream 527 void TransformStream::Initialize(JSContext* aCx, Promise* aStartPromise, 528 double aWritableHighWaterMark, 529 QueuingStrategySize* aWritableSizeAlgorithm, 530 double aReadableHighWaterMark, 531 QueuingStrategySize* aReadableSizeAlgorithm, 532 ErrorResult& aRv) { 533 // Step 1 - 4 534 auto sinkAlgorithms = 535 MakeRefPtr<TransformStreamUnderlyingSinkAlgorithms>(aStartPromise, this); 536 537 // Step 5. Set stream.[[writable]] to ! CreateWritableStream(startAlgorithm, 538 // writeAlgorithm, closeAlgorithm, abortAlgorithm, writableHighWaterMark, 539 // writableSizeAlgorithm). 540 mWritable = WritableStream::CreateAbstract( 541 aCx, MOZ_KnownLive(mGlobal), sinkAlgorithms, aWritableHighWaterMark, 542 aWritableSizeAlgorithm, aRv); 543 if (aRv.Failed()) { 544 return; 545 } 546 547 // Step 6 - 7 548 auto sourceAlgorithms = MakeRefPtr<TransformStreamUnderlyingSourceAlgorithms>( 549 aStartPromise, this); 550 551 // Step 8. Set stream.[[readable]] to ! CreateReadableStream(startAlgorithm, 552 // pullAlgorithm, cancelAlgorithm, readableHighWaterMark, 553 // readableSizeAlgorithm). 554 mReadable = ReadableStream::CreateAbstract( 555 aCx, MOZ_KnownLive(mGlobal), sourceAlgorithms, 556 Some(aReadableHighWaterMark), aReadableSizeAlgorithm, aRv); 557 if (aRv.Failed()) { 558 return; 559 } 560 561 // Step 9. Set stream.[[backpressure]] and 562 // stream.[[backpressureChangePromise]] to undefined. 563 // Note(krosylight): The spec allows setting [[backpressure]] as undefined, 564 // but I don't see why it should be. Since the spec also allows strict boolean 565 // type, and this is only to not trigger assertion inside the setter, we just 566 // set it as false. 567 mBackpressure = false; 568 mBackpressureChangePromise = nullptr; 569 570 // Step 10. Perform ! TransformStreamSetBackpressure(stream, true). 571 SetBackpressure(true); 572 if (aRv.Failed()) { 573 return; 574 } 575 576 // Step 11. Set stream.[[controller]] to undefined. 577 mController = nullptr; 578 } 579 580 // https://streams.spec.whatwg.org/#ts-constructor 581 already_AddRefed<TransformStream> TransformStream::Constructor( 582 const GlobalObject& aGlobal, 583 const Optional<JS::Handle<JSObject*>>& aTransformer, 584 const QueuingStrategy& aWritableStrategy, 585 const QueuingStrategy& aReadableStrategy, ErrorResult& aRv) { 586 // Step 1. If transformer is missing, set it to null. 587 JS::Rooted<JSObject*> transformerObj( 588 aGlobal.Context(), 589 aTransformer.WasPassed() ? aTransformer.Value() : nullptr); 590 591 // Step 2. Let transformerDict be transformer, converted to an IDL value of 592 // type Transformer. 593 RootedDictionary<Transformer> transformerDict(aGlobal.Context()); 594 if (transformerObj) { 595 JS::Rooted<JS::Value> objValue(aGlobal.Context(), 596 JS::ObjectValue(*transformerObj)); 597 dom::BindingCallContext callCx(aGlobal.Context(), 598 "TransformStream.constructor"); 599 aRv.MightThrowJSException(); 600 if (!transformerDict.Init(callCx, objValue)) { 601 aRv.StealExceptionFromJSContext(aGlobal.Context()); 602 return nullptr; 603 } 604 } 605 606 // Step 3. If transformerDict["readableType"] exists, throw a RangeError 607 // exception. 608 if (!transformerDict.mReadableType.isUndefined()) { 609 aRv.ThrowRangeError( 610 "`readableType` is unsupported and preserved for future use"); 611 return nullptr; 612 } 613 614 // Step 4. If transformerDict["writableType"] exists, throw a RangeError 615 // exception. 616 if (!transformerDict.mWritableType.isUndefined()) { 617 aRv.ThrowRangeError( 618 "`writableType` is unsupported and preserved for future use"); 619 return nullptr; 620 } 621 622 // Step 5. Let readableHighWaterMark be ? 623 // ExtractHighWaterMark(readableStrategy, 0). 624 double readableHighWaterMark = 625 ExtractHighWaterMark(aReadableStrategy, 0, aRv); 626 if (aRv.Failed()) { 627 return nullptr; 628 } 629 630 // Step 6. Let readableSizeAlgorithm be ! 631 // ExtractSizeAlgorithm(readableStrategy). 632 // Note: Callers should recognize nullptr as a callback that returns 1. See 633 // also ReadableStream::Constructor for this design decision. 634 RefPtr<QueuingStrategySize> readableSizeAlgorithm = 635 aReadableStrategy.mSize.WasPassed() ? &aReadableStrategy.mSize.Value() 636 : nullptr; 637 638 // Step 7. Let writableHighWaterMark be ? 639 // ExtractHighWaterMark(writableStrategy, 1). 640 double writableHighWaterMark = 641 ExtractHighWaterMark(aWritableStrategy, 1, aRv); 642 if (aRv.Failed()) { 643 return nullptr; 644 } 645 646 // Step 8. Let writableSizeAlgorithm be ! 647 // ExtractSizeAlgorithm(writableStrategy). 648 // Note: Callers should recognize nullptr as a callback that returns 1. See 649 // also WritableStream::Constructor for this design decision. 650 RefPtr<QueuingStrategySize> writableSizeAlgorithm = 651 aWritableStrategy.mSize.WasPassed() ? &aWritableStrategy.mSize.Value() 652 : nullptr; 653 654 // Step 9. Let startPromise be a new promise. 655 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 656 RefPtr<Promise> startPromise = Promise::CreateInfallible(global); 657 658 // Step 10. Perform ! InitializeTransformStream(this, startPromise, 659 // writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark, 660 // readableSizeAlgorithm). 661 RefPtr<TransformStream> transformStream = new TransformStream(global); 662 transformStream->Initialize( 663 aGlobal.Context(), startPromise, writableHighWaterMark, 664 writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm, aRv); 665 if (aRv.Failed()) { 666 return nullptr; 667 } 668 669 // Step 11. Perform ? 670 // SetUpTransformStreamDefaultControllerFromTransformer(this, transformer, 671 // transformerDict). 672 SetUpTransformStreamDefaultControllerFromTransformer( 673 aGlobal.Context(), *transformStream, transformerObj, transformerDict); 674 675 // Step 12. If transformerDict["start"] exists, then resolve startPromise with 676 // the result of invoking transformerDict["start"] with argument list « 677 // this.[[controller]] » and callback this value transformer. 678 if (transformerDict.mStart.WasPassed()) { 679 RefPtr<TransformerStartCallback> callback = transformerDict.mStart.Value(); 680 RefPtr<TransformStreamDefaultController> controller = 681 transformStream->Controller(); 682 JS::Rooted<JS::Value> retVal(aGlobal.Context()); 683 callback->Call(transformerObj, *controller, &retVal, aRv, 684 "Transformer.start", CallbackFunction::eRethrowExceptions); 685 if (aRv.Failed()) { 686 return nullptr; 687 } 688 689 startPromise->MaybeResolve(retVal); 690 } else { 691 // Step 13. Otherwise, resolve startPromise with undefined. 692 startPromise->MaybeResolveWithUndefined(); 693 } 694 695 return transformStream.forget(); 696 } 697 698 } // namespace mozilla::dom