Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / streams / TransformStream.cpp
blob3b78cabeea4aeb59eb3c1ccb7fc9241499328548
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/TransformStream.h"
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.h"
15 #include "mozilla/dom/Promise-inl.h"
16 #include "mozilla/dom/WritableStream.h"
17 #include "mozilla/dom/ReadableStream.h"
18 #include "mozilla/dom/RootedDictionary.h"
19 #include "mozilla/dom/TransformStreamBinding.h"
20 #include "mozilla/dom/TransformerBinding.h"
21 #include "nsWrapperCache.h"
23 namespace mozilla::dom {
25 using namespace streams_abstract;
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
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;
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;
51 // Step 3. Let readableHighWaterMark be 0.
52 double readableHighWaterMark = 0;
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;
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)
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;
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;
84 // Step 9. Let controller be a new TransformStreamDefaultController.
85 auto controller = MakeRefPtr<TransformStreamDefaultController>(global);
87 // Step 10. Perform ! SetUpTransformStreamDefaultController(stream,
88 // controller, transformAlgorithmWrapper, flushAlgorithmWrapper).
89 SetUpTransformStreamDefaultController(aGlobal.Context(), *stream, *controller,
90 aAlgorithms);
92 return stream.forget();
95 TransformStream::TransformStream(nsIGlobalObject* aGlobal) : mGlobal(aGlobal) {
96 mozilla::HoldJSObjects(this);
99 TransformStream::TransformStream(nsIGlobalObject* aGlobal,
100 ReadableStream* aReadable,
101 WritableStream* aWritable)
102 : mGlobal(aGlobal), mReadable(aReadable), mWritable(aWritable) {
103 mozilla::HoldJSObjects(this);
106 TransformStream::~TransformStream() { mozilla::DropJSObjects(this); }
108 JSObject* TransformStream::WrapObject(JSContext* aCx,
109 JS::Handle<JSObject*> aGivenProto) {
110 return TransformStream_Binding::Wrap(aCx, this, aGivenProto);
113 namespace streams_abstract {
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);
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;
134 // Step 3: If stream.[[backpressure]] is true, perform !
135 // TransformStreamSetBackpressure(stream, false).
136 if (aStream->Backpressure()) {
137 aStream->SetBackpressure(false);
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;
153 // Step 2: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, e).
154 TransformStreamErrorWritableAndUnblockWrite(aCx, aStream, aError, aRv);
157 } // namespace streams_abstract
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;
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;
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;
194 RefPtr(aController));
195 if (result.isErr()) {
196 aRv.Throw(result.unwrapErr());
197 return nullptr;
199 return result.unwrap().forget();
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)
210 TransformStreamUnderlyingSinkAlgorithms(Promise* aStartPromise,
211 TransformStream* aStream)
212 : mStartPromise(aStartPromise), mStream(aStream) {}
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());
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).
231 // Inlining TransformStreamDefaultSinkWriteAlgorithm here:
232 // https://streams.spec.whatwg.org/#transform-stream-default-sink-write-algorithm
234 // Step 1: Assert: stream.[[writable]].[[state]] is "writable".
235 MOZ_ASSERT(mStream->Writable()->State() ==
236 WritableStream::WriterState::Writable);
238 // Step 2: Let controller be stream.[[controller]].
239 RefPtr<TransformStreamDefaultController> controller = mStream->Controller();
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();
248 // Step 3.2: Assert: backpressureChangePromise is not undefined.
249 MOZ_ASSERT(backpressureChangePromise);
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();
262 // Step 2: Let state be writable.[[state]].
263 WritableStream::WriterState state = writable->State();
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;
275 // Step 4: Assert: state is "writable".
276 MOZ_ASSERT(state == WritableStream::WriterState::Writable);
278 // Step 5: Return !
279 // TransformStreamDefaultControllerPerformTransform(controller,
280 // chunk).
281 return TransformStreamDefaultControllerPerformTransform(
282 aCx, aController, aChunk, aRv);
284 std::make_tuple(mStream, controller), std::make_tuple(aChunk));
286 if (result.isErr()) {
287 aRv.Throw(result.unwrapErr());
288 return nullptr;
290 return result.unwrap().forget();
293 // Step 4: Return !
294 // TransformStreamDefaultControllerPerformTransform(controller, chunk).
295 return TransformStreamDefaultControllerPerformTransform(aCx, controller,
296 aChunk, aRv);
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).
307 // Inlining TransformStreamDefaultSinkAbortAlgorithm here:
308 // https://streams.spec.whatwg.org/#transform-stream-default-sink-abort-algorithm
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;
318 // Step 2: Return a promise resolved with undefined.
319 return Promise::CreateResolvedWithUndefined(mStream->GetParentObject(),
320 aRv);
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).
328 // Inlining TransformStreamDefaultSinkCloseAlgorithm here:
329 // https://streams.spec.whatwg.org/#transform-stream-default-sink-close-algorithm
331 // Step 1: Let readable be stream.[[readable]].
332 RefPtr<ReadableStream> readable = mStream->Readable();
334 // Step 2: Let controller be stream.[[controller]].
335 RefPtr<TransformStreamDefaultController> controller = mStream->Controller();
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;
346 // Step 4: Perform !
347 // TransformStreamDefaultControllerClearAlgorithms(controller).
348 controller->SetAlgorithms(nullptr);
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:
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;
371 // Step 5.1.2: Perform !
372 // ReadableStreamDefaultControllerClose(readable.[[controller]]).
373 ReadableStreamDefaultControllerClose(
374 aCx, MOZ_KnownLive(aReadable->Controller()->AsDefault()),
375 aRv);
376 return nullptr;
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:
385 // Step 5.2.1: Perform ! TransformStreamError(stream, r).
386 TransformStreamError(aCx, aStream, aValue, aRv);
387 if (aRv.Failed()) {
388 return nullptr;
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;
398 readable, mStream);
400 if (result.isErr()) {
401 aRv.Throw(result.unwrapErr());
402 return nullptr;
404 return result.unwrap().forget();
407 protected:
408 ~TransformStreamUnderlyingSinkAlgorithms() override = default;
410 private:
411 RefPtr<Promise> mStartPromise;
412 // MOZ_KNOWN_LIVE because it won't be reassigned
413 MOZ_KNOWN_LIVE RefPtr<TransformStream> mStream;
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)
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)
434 TransformStreamUnderlyingSourceAlgorithms(Promise* aStartPromise,
435 TransformStream* aStream)
436 : mStartPromise(aStartPromise), mStream(aStream) {}
438 void StartCallback(JSContext* aCx, ReadableStreamController& 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());
446 already_AddRefed<Promise> PullCallback(JSContext* aCx,
447 ReadableStreamController& aController,
448 ErrorResult& aRv) override {
449 // Step 6. Let pullAlgorithm be the following steps:
450 // Step 6.1. Return ! TransformStreamDefaultSourcePullAlgorithm(stream).
452 // Inlining TransformStreamDefaultSourcePullAlgorithm here:
453 // https://streams.spec.whatwg.org/#transform-stream-default-source-pull-algorithm
455 // Step 1: Assert: stream.[[backpressure]] is true.
456 MOZ_ASSERT(mStream->Backpressure());
458 // Step 2: Assert: stream.[[backpressureChangePromise]] is not undefined.
459 MOZ_ASSERT(mStream->BackpressureChangePromise());
461 // Step 3: Perform ! TransformStreamSetBackpressure(stream, false).
462 mStream->SetBackpressure(false);
464 // Step 4: Return stream.[[backpressureChangePromise]].
465 return do_AddRef(mStream->BackpressureChangePromise());
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;
482 // Step 7.2. Return a promise resolved with undefined.
483 return Promise::CreateResolvedWithUndefined(mStream->GetParentObject(),
484 aRv);
487 protected:
488 ~TransformStreamUnderlyingSourceAlgorithms() override = default;
490 private:
491 RefPtr<Promise> mStartPromise;
492 // MOZ_KNOWNLIVE because it will never be reassigned
493 MOZ_KNOWN_LIVE RefPtr<TransformStream> mStream;
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)
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);
512 // Step 2. If stream.[[backpressureChangePromise]] is not undefined, resolve
513 // stream.[[backpressureChangePromise]] with undefined.
514 if (Promise* promise = BackpressureChangePromise()) {
515 promise->MaybeResolveWithUndefined();
518 // Step 3. Set stream.[[backpressureChangePromise]] to a new promise.
519 RefPtr<Promise> promise = Promise::CreateInfallible(GetParentObject());
520 mBackpressureChangePromise = promise;
522 // Step 4. Set stream.[[backpressure]] to backpressure.
523 mBackpressure = aBackpressure;
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);
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;
547 // Step 6 - 7
548 auto sourceAlgorithms = MakeRefPtr<TransformStreamUnderlyingSourceAlgorithms>(
549 aStartPromise, this);
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;
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;
570 // Step 10. Perform ! TransformStreamSetBackpressure(stream, true).
571 SetBackpressure(true);
572 if (aRv.Failed()) {
573 return;
576 // Step 11. Set stream.[[controller]] to undefined.
577 mController = nullptr;
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);
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;
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;
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;
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;
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;
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;
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;
654 // Step 9. Let startPromise be a new promise.
655 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
656 RefPtr<Promise> startPromise = Promise::CreateInfallible(global);
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;
669 // Step 11. Perform ?
670 // SetUpTransformStreamDefaultControllerFromTransformer(this, transformer,
671 // transformerDict).
672 SetUpTransformStreamDefaultControllerFromTransformer(
673 aGlobal.Context(), *transformStream, transformerObj, transformerDict);
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;
689 startPromise->MaybeResolve(retVal);
690 } else {
691 // Step 13. Otherwise, resolve startPromise with undefined.
692 startPromise->MaybeResolveWithUndefined();
695 return transformStream.forget();
698 } // namespace mozilla::dom