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
,
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
)
37 // https://streams.spec.whatwg.org/#transformstream-set-up
38 // (except this instead creates a new TransformStream rather than accepting an
40 already_AddRefed
<TransformStream
> TransformStream::CreateGeneric(
41 const GlobalObject
& aGlobal
, TransformerAlgorithmsWrapper
& aAlgorithms
,
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
);
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
);
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
,
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
,
121 // TransformStreamDefaultControllerClearAlgorithms(stream.[[controller]]).
122 aStream
->Controller()->SetAlgorithms(nullptr);
125 // WritableStreamDefaultControllerErrorIfNeeded(stream.[[writable]].[[controller]],
127 // TODO: Remove MOZ_KnownLive (bug 1761577)
128 WritableStreamDefaultControllerErrorIfNeeded(
129 aCx
, MOZ_KnownLive(aStream
->Writable()->Controller()), aError
, aRv
);
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
) {
145 // ReadableStreamDefaultControllerError(stream.[[readable]].[[controller]],
147 ReadableStreamDefaultControllerError(
148 aCx
, aStream
->Readable()->Controller()->AsDefault(), aError
, aRv
);
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
);
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]],
181 // TODO: Remove MOZ_KnownLive (bug 1761577)
182 TransformStreamError(aCx
, MOZ_KnownLive(aController
->Stream()),
188 // Step 2.2: Throw r.
189 JS::Rooted
<JS::Value
> r(aCx
, aError
);
190 aRv
.MightThrowJSException();
191 aRv
.ThrowJSException(aCx
, r
);
194 RefPtr(aController
));
195 if (result
.isErr()) {
196 aRv
.Throw(result
.unwrapErr());
199 return result
.unwrap().forget();
202 // https://streams.spec.whatwg.org/#initialize-transform-stream
203 class TransformStreamUnderlyingSinkAlgorithms final
204 : public UnderlyingSinkAlgorithmsBase
{
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
228 // Step 2.1. Return ! TransformStreamDefaultSinkWriteAlgorithm(stream,
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
);
275 // Step 4: Assert: state is "writable".
276 MOZ_ASSERT(state
== WritableStream::WriterState::Writable
);
279 // TransformStreamDefaultControllerPerformTransform(controller,
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());
290 return result
.unwrap().forget();
294 // TransformStreamDefaultControllerPerformTransform(controller, chunk).
295 return TransformStreamDefaultControllerPerformTransform(aCx
, controller
,
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
304 // Step 3.1. Return ! TransformStreamDefaultSinkAbortAlgorithm(stream,
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(
313 aReason
.WasPassed() ? aReason
.Value() : JS::UndefinedHandleValue
, aRv
);
318 // Step 2: Return a promise resolved with undefined.
319 return Promise::CreateResolvedWithUndefined(mStream
->GetParentObject(),
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
);
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
);
371 // Step 5.1.2: Perform !
372 // ReadableStreamDefaultControllerClose(readable.[[controller]]).
373 ReadableStreamDefaultControllerClose(
374 aCx
, MOZ_KnownLive(aReadable
->Controller()->AsDefault()),
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
);
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
);
400 if (result
.isErr()) {
401 aRv
.Throw(result
.unwrapErr());
404 return result
.unwrap().forget();
408 ~TransformStreamUnderlyingSinkAlgorithms() override
= default;
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
,
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
{
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
473 // Step 7.1. Perform ! TransformStreamErrorWritableAndUnblockWrite(stream,
475 TransformStreamErrorWritableAndUnblockWrite(
477 aReason
.WasPassed() ? aReason
.Value() : JS::UndefinedHandleValue
, aRv
);
482 // Step 7.2. Return a promise resolved with undefined.
483 return Promise::CreateResolvedWithUndefined(mStream
->GetParentObject(),
488 ~TransformStreamUnderlyingSourceAlgorithms() override
= default;
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
,
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
);
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
);
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
567 mBackpressure
= false;
568 mBackpressureChangePromise
= nullptr;
570 // Step 10. Perform ! TransformStreamSetBackpressure(stream, true).
571 SetBackpressure(true);
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(
589 aTransformer
.WasPassed() ? aTransformer
.Value() : nullptr);
591 // Step 2. Let transformerDict be transformer, converted to an IDL value of
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());
606 // Step 3. If transformerDict["readableType"] exists, throw a RangeError
608 if (!transformerDict
.mReadableType
.isUndefined()) {
610 "`readableType` is unsupported and preserved for future use");
614 // Step 4. If transformerDict["writableType"] exists, throw a RangeError
616 if (!transformerDict
.mWritableType
.isUndefined()) {
618 "`writableType` is unsupported and preserved for future use");
622 // Step 5. Let readableHighWaterMark be ?
623 // ExtractHighWaterMark(readableStrategy, 0).
624 double readableHighWaterMark
=
625 ExtractHighWaterMark(aReadableStrategy
, 0, aRv
);
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()
638 // Step 7. Let writableHighWaterMark be ?
639 // ExtractHighWaterMark(writableStrategy, 1).
640 double writableHighWaterMark
=
641 ExtractHighWaterMark(aWritableStrategy
, 1, aRv
);
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()
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
);
669 // Step 11. Perform ?
670 // SetUpTransformStreamDefaultControllerFromTransformer(this, transformer,
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
);
689 startPromise
->MaybeResolve(retVal
);
691 // Step 13. Otherwise, resolve startPromise with undefined.
692 startPromise
->MaybeResolveWithUndefined();
695 return transformStream
.forget();
698 } // namespace mozilla::dom