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 // XXX: GCC somehow does not allow attributes before lambda return types, while
24 // clang requires so. See also bug 1627007.
26 # define MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA MOZ_CAN_RUN_SCRIPT_BOUNDARY
28 # define MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
31 namespace mozilla::dom
{
33 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TransformStream
, mGlobal
,
34 mBackpressureChangePromise
, mController
,
36 NS_IMPL_CYCLE_COLLECTING_ADDREF(TransformStream
)
37 NS_IMPL_CYCLE_COLLECTING_RELEASE(TransformStream
)
38 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStream
)
39 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
40 NS_INTERFACE_MAP_ENTRY(nsISupports
)
43 // https://streams.spec.whatwg.org/#transformstream-set-up
44 // (except this instead creates a new TransformStream rather than accepting an
46 already_AddRefed
<TransformStream
> TransformStream::CreateGeneric(
47 const GlobalObject
& aGlobal
, TransformerAlgorithmsWrapper
& aAlgorithms
,
49 // Step 1. Let writableHighWaterMark be 1.
50 double writableHighWaterMark
= 1;
52 // Step 2. Let writableSizeAlgorithm be an algorithm that returns 1.
53 // Note: Callers should recognize nullptr as a callback that returns 1. See
54 // also WritableStream::Constructor for this design decision.
55 RefPtr
<QueuingStrategySize
> writableSizeAlgorithm
;
57 // Step 3. Let readableHighWaterMark be 0.
58 double readableHighWaterMark
= 0;
60 // Step 4. Let readableSizeAlgorithm be an algorithm that returns 1.
61 // Note: Callers should recognize nullptr as a callback that returns 1. See
62 // also ReadableStream::Constructor for this design decision.
63 RefPtr
<QueuingStrategySize
> readableSizeAlgorithm
;
65 // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps
66 // given a value chunk:
67 // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps:
68 // (Done by TransformerAlgorithmsWrapper)
70 // Step 7. Let startPromise be a promise resolved with undefined.
71 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
72 RefPtr
<Promise
> startPromise
=
73 Promise::CreateResolvedWithUndefined(global
, aRv
);
78 // Step 8. Perform ! InitializeTransformStream(stream, startPromise,
79 // writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark,
80 // readableSizeAlgorithm).
81 auto stream
= MakeRefPtr
<TransformStream
>(global
, nullptr, nullptr);
82 stream
->Initialize(aGlobal
.Context(), startPromise
, writableHighWaterMark
,
83 writableSizeAlgorithm
, readableHighWaterMark
,
84 readableSizeAlgorithm
, aRv
);
89 // Step 9. Let controller be a new TransformStreamDefaultController.
90 auto controller
= MakeRefPtr
<TransformStreamDefaultController
>(global
);
92 // Step 10. Perform ! SetUpTransformStreamDefaultController(stream,
93 // controller, transformAlgorithmWrapper, flushAlgorithmWrapper).
94 SetUpTransformStreamDefaultController(aGlobal
.Context(), *stream
, *controller
,
97 return stream
.forget();
100 TransformStream::TransformStream(nsIGlobalObject
* aGlobal
) : mGlobal(aGlobal
) {
101 mozilla::HoldJSObjects(this);
104 TransformStream::TransformStream(nsIGlobalObject
* aGlobal
,
105 ReadableStream
* aReadable
,
106 WritableStream
* aWritable
)
107 : mGlobal(aGlobal
), mReadable(aReadable
), mWritable(aWritable
) {
108 mozilla::HoldJSObjects(this);
111 TransformStream::~TransformStream() { mozilla::DropJSObjects(this); }
113 JSObject
* TransformStream::WrapObject(JSContext
* aCx
,
114 JS::Handle
<JSObject
*> aGivenProto
) {
115 return TransformStream_Binding::Wrap(aCx
, this, aGivenProto
);
118 // https://streams.spec.whatwg.org/#transform-stream-error-writable-and-unblock-write
119 void TransformStreamErrorWritableAndUnblockWrite(JSContext
* aCx
,
120 TransformStream
* aStream
,
121 JS::Handle
<JS::Value
> aError
,
124 // TransformStreamDefaultControllerClearAlgorithms(stream.[[controller]]).
125 aStream
->Controller()->SetAlgorithms(nullptr);
128 // WritableStreamDefaultControllerErrorIfNeeded(stream.[[writable]].[[controller]],
130 // TODO: Remove MOZ_KnownLive (bug 1761577)
131 WritableStreamDefaultControllerErrorIfNeeded(
132 aCx
, MOZ_KnownLive(aStream
->Writable()->Controller()), aError
, aRv
);
137 // Step 3: If stream.[[backpressure]] is true, perform !
138 // TransformStreamSetBackpressure(stream, false).
139 if (aStream
->Backpressure()) {
140 aStream
->SetBackpressure(false);
144 // https://streams.spec.whatwg.org/#transform-stream-error
145 void TransformStreamError(JSContext
* aCx
, TransformStream
* aStream
,
146 JS::Handle
<JS::Value
> aError
, ErrorResult
& aRv
) {
148 // ReadableStreamDefaultControllerError(stream.[[readable]].[[controller]],
150 ReadableStreamDefaultControllerError(
151 aCx
, aStream
->Readable()->Controller()->AsDefault(), aError
, aRv
);
156 // Step 2: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, e).
157 TransformStreamErrorWritableAndUnblockWrite(aCx
, aStream
, aError
, aRv
);
160 // https://streams.spec.whatwg.org/#transform-stream-default-controller-perform-transform
161 MOZ_CAN_RUN_SCRIPT
static already_AddRefed
<Promise
>
162 TransformStreamDefaultControllerPerformTransform(
163 JSContext
* aCx
, TransformStreamDefaultController
* aController
,
164 JS::Handle
<JS::Value
> aChunk
, ErrorResult
& aRv
) {
165 // Step 1: Let transformPromise be the result of performing
166 // controller.[[transformAlgorithm]], passing chunk.
167 RefPtr
<TransformerAlgorithmsBase
> algorithms
= aController
->Algorithms();
168 RefPtr
<Promise
> transformPromise
=
169 algorithms
->TransformCallback(aCx
, aChunk
, *aController
, aRv
);
174 // Step 2: Return the result of reacting to transformPromise with the
175 // following rejection steps given the argument r:
176 auto result
= transformPromise
->CatchWithCycleCollectedArgs(
177 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aError
, ErrorResult
& aRv
,
178 const RefPtr
<TransformStreamDefaultController
>& aController
)
179 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
-> already_AddRefed
<Promise
> {
180 // Step 2.1: Perform ! TransformStreamError(controller.[[stream]],
182 // TODO: Remove MOZ_KnownLive (bug 1761577)
183 TransformStreamError(aCx
, MOZ_KnownLive(aController
->Stream()),
189 // Step 2.2: Throw r.
190 JS::Rooted
<JS::Value
> r(aCx
, aError
);
191 aRv
.MightThrowJSException();
192 aRv
.ThrowJSException(aCx
, r
);
195 RefPtr(aController
));
196 if (result
.isErr()) {
197 aRv
.Throw(result
.unwrapErr());
200 return result
.unwrap().forget();
203 // https://streams.spec.whatwg.org/#initialize-transform-stream
204 class TransformStreamUnderlyingSinkAlgorithms final
205 : public UnderlyingSinkAlgorithmsBase
{
207 NS_DECL_ISUPPORTS_INHERITED
208 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
209 TransformStreamUnderlyingSinkAlgorithms
, UnderlyingSinkAlgorithmsBase
)
211 TransformStreamUnderlyingSinkAlgorithms(Promise
* aStartPromise
,
212 TransformStream
* aStream
)
213 : mStartPromise(aStartPromise
), mStream(aStream
) {}
215 void StartCallback(JSContext
* aCx
,
216 WritableStreamDefaultController
& aController
,
217 JS::MutableHandle
<JS::Value
> aRetVal
,
218 ErrorResult
& aRv
) override
{
219 // Step 1. Let startAlgorithm be an algorithm that returns startPromise.
220 // (Same as TransformStreamUnderlyingSourceAlgorithms::StartCallback)
221 aRetVal
.setObject(*mStartPromise
->PromiseObj());
224 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> WriteCallback(
225 JSContext
* aCx
, JS::Handle
<JS::Value
> aChunk
,
226 WritableStreamDefaultController
& aController
, ErrorResult
& aRv
) override
{
227 // Step 2. Let writeAlgorithm be the following steps, taking a chunk
229 // Step 2.1. Return ! TransformStreamDefaultSinkWriteAlgorithm(stream,
232 // Inlining TransformStreamDefaultSinkWriteAlgorithm here:
233 // https://streams.spec.whatwg.org/#transform-stream-default-sink-write-algorithm
235 // Step 1: Assert: stream.[[writable]].[[state]] is "writable".
236 MOZ_ASSERT(mStream
->Writable()->State() ==
237 WritableStream::WriterState::Writable
);
239 // Step 2: Let controller be stream.[[controller]].
240 RefPtr
<TransformStreamDefaultController
> controller
= mStream
->Controller();
242 // Step 3: If stream.[[backpressure]] is true,
243 if (mStream
->Backpressure()) {
244 // Step 3.1: Let backpressureChangePromise be
245 // stream.[[backpressureChangePromise]].
246 RefPtr
<Promise
> backpressureChangePromise
=
247 mStream
->BackpressureChangePromise();
249 // Step 3.2: Assert: backpressureChangePromise is not undefined.
250 MOZ_ASSERT(backpressureChangePromise
);
252 // Step 3.3: Return the result of reacting to backpressureChangePromise
253 // with the following fulfillment steps:
254 auto result
= backpressureChangePromise
->ThenWithCycleCollectedArgsJS(
255 [](JSContext
* aCx
, JS::Handle
<JS::Value
>, ErrorResult
& aRv
,
256 const RefPtr
<TransformStream
>& aStream
,
257 const RefPtr
<TransformStreamDefaultController
>& aController
,
258 JS::Handle
<JS::Value
> aChunk
)
259 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
-> already_AddRefed
<Promise
> {
260 // Step 1: Let writable be stream.[[writable]].
261 RefPtr
<WritableStream
> writable
= aStream
->Writable();
263 // Step 2: Let state be writable.[[state]].
264 WritableStream::WriterState state
= writable
->State();
266 // Step 3: If state is "erroring", throw
267 // writable.[[storedError]].
268 if (state
== WritableStream::WriterState::Erroring
) {
269 JS::Rooted
<JS::Value
> storedError(aCx
,
270 writable
->StoredError());
271 aRv
.MightThrowJSException();
272 aRv
.ThrowJSException(aCx
, storedError
);
276 // Step 4: Assert: state is "writable".
277 MOZ_ASSERT(state
== WritableStream::WriterState::Writable
);
280 // TransformStreamDefaultControllerPerformTransform(controller,
282 return TransformStreamDefaultControllerPerformTransform(
283 aCx
, aController
, aChunk
, aRv
);
285 std::make_tuple(mStream
, controller
), std::make_tuple(aChunk
));
287 if (result
.isErr()) {
288 aRv
.Throw(result
.unwrapErr());
291 return result
.unwrap().forget();
295 // TransformStreamDefaultControllerPerformTransform(controller, chunk).
296 return TransformStreamDefaultControllerPerformTransform(aCx
, controller
,
300 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> AbortCallback(
301 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
302 ErrorResult
& aRv
) override
{
303 // Step 3. Let abortAlgorithm be the following steps, taking a reason
305 // Step 3.1. Return ! TransformStreamDefaultSinkAbortAlgorithm(stream,
308 // Inlining TransformStreamDefaultSinkAbortAlgorithm here:
309 // https://streams.spec.whatwg.org/#transform-stream-default-sink-abort-algorithm
311 // Step 1:Perform ! TransformStreamError(stream, reason).
312 TransformStreamError(
314 aReason
.WasPassed() ? aReason
.Value() : JS::UndefinedHandleValue
, aRv
);
319 // Step 2: Return a promise resolved with undefined.
320 return Promise::CreateResolvedWithUndefined(mStream
->GetParentObject(),
324 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> CloseCallback(
325 JSContext
* aCx
, ErrorResult
& aRv
) override
{
326 // Step 4. Let closeAlgorithm be the following steps:
327 // Step 4.1. Return ! TransformStreamDefaultSinkCloseAlgorithm(stream).
329 // Inlining TransformStreamDefaultSinkCloseAlgorithm here:
330 // https://streams.spec.whatwg.org/#transform-stream-default-sink-close-algorithm
332 // Step 1: Let readable be stream.[[readable]].
333 RefPtr
<ReadableStream
> readable
= mStream
->Readable();
335 // Step 2: Let controller be stream.[[controller]].
336 RefPtr
<TransformStreamDefaultController
> controller
= mStream
->Controller();
338 // Step 3: Let flushPromise be the result of performing
339 // controller.[[flushAlgorithm]].
340 RefPtr
<TransformerAlgorithmsBase
> algorithms
= controller
->Algorithms();
341 RefPtr
<Promise
> flushPromise
=
342 algorithms
->FlushCallback(aCx
, *controller
, aRv
);
348 // TransformStreamDefaultControllerClearAlgorithms(controller).
349 controller
->SetAlgorithms(nullptr);
351 // Step 5: Return the result of reacting to flushPromise:
352 Result
<RefPtr
<Promise
>, nsresult
> result
=
353 flushPromise
->ThenCatchWithCycleCollectedArgs(
354 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
,
355 const RefPtr
<ReadableStream
>& aReadable
,
356 const RefPtr
<TransformStream
>& aStream
)
357 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
358 -> already_AddRefed
<Promise
> {
359 // Step 5.1: If flushPromise was fulfilled, then:
361 // Step 5.1.1: If readable.[[state]] is "errored", throw
362 // readable.[[storedError]].
363 if (aReadable
->State() ==
364 ReadableStream::ReaderState::Errored
) {
365 JS::Rooted
<JS::Value
> storedError(aCx
,
366 aReadable
->StoredError());
367 aRv
.MightThrowJSException();
368 aRv
.ThrowJSException(aCx
, storedError
);
372 // Step 5.1.2: Perform !
373 // ReadableStreamDefaultControllerClose(readable.[[controller]]).
374 ReadableStreamDefaultControllerClose(
375 aCx
, MOZ_KnownLive(aReadable
->Controller()->AsDefault()),
379 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
,
380 const RefPtr
<ReadableStream
>& aReadable
,
381 const RefPtr
<TransformStream
>& aStream
)
382 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
383 -> already_AddRefed
<Promise
> {
384 // Step 5.2: If flushPromise was rejected with reason r, then:
386 // Step 5.2.1: Perform ! TransformStreamError(stream, r).
387 TransformStreamError(aCx
, aStream
, aValue
, aRv
);
392 // Step 5.2.2: Throw readable.[[storedError]].
393 JS::Rooted
<JS::Value
> storedError(aCx
,
394 aReadable
->StoredError());
395 aRv
.MightThrowJSException();
396 aRv
.ThrowJSException(aCx
, storedError
);
401 if (result
.isErr()) {
402 aRv
.Throw(result
.unwrapErr());
405 return result
.unwrap().forget();
409 ~TransformStreamUnderlyingSinkAlgorithms() override
= default;
412 RefPtr
<Promise
> mStartPromise
;
413 // MOZ_KNOWN_LIVE because it won't be reassigned
414 MOZ_KNOWN_LIVE RefPtr
<TransformStream
> mStream
;
417 NS_IMPL_CYCLE_COLLECTION_INHERITED(TransformStreamUnderlyingSinkAlgorithms
,
418 UnderlyingSinkAlgorithmsBase
, mStartPromise
,
420 NS_IMPL_ADDREF_INHERITED(TransformStreamUnderlyingSinkAlgorithms
,
421 UnderlyingSinkAlgorithmsBase
)
422 NS_IMPL_RELEASE_INHERITED(TransformStreamUnderlyingSinkAlgorithms
,
423 UnderlyingSinkAlgorithmsBase
)
424 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStreamUnderlyingSinkAlgorithms
)
425 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSinkAlgorithmsBase
)
427 // https://streams.spec.whatwg.org/#initialize-transform-stream
428 class TransformStreamUnderlyingSourceAlgorithms final
429 : public UnderlyingSourceAlgorithmsBase
{
431 NS_DECL_ISUPPORTS_INHERITED
432 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
433 TransformStreamUnderlyingSourceAlgorithms
, UnderlyingSourceAlgorithmsBase
)
435 TransformStreamUnderlyingSourceAlgorithms(Promise
* aStartPromise
,
436 TransformStream
* aStream
)
437 : mStartPromise(aStartPromise
), mStream(aStream
) {}
439 void StartCallback(JSContext
* aCx
, ReadableStreamController
& aController
,
440 JS::MutableHandle
<JS::Value
> aRetVal
,
441 ErrorResult
& aRv
) override
{
442 // Step 1. Let startAlgorithm be an algorithm that returns startPromise.
443 // (Same as TransformStreamUnderlyingSinkAlgorithms::StartCallback)
444 aRetVal
.setObject(*mStartPromise
->PromiseObj());
447 already_AddRefed
<Promise
> PullCallback(JSContext
* aCx
,
448 ReadableStreamController
& aController
,
449 ErrorResult
& aRv
) override
{
450 // Step 6. Let pullAlgorithm be the following steps:
451 // Step 6.1. Return ! TransformStreamDefaultSourcePullAlgorithm(stream).
453 // Inlining TransformStreamDefaultSourcePullAlgorithm here:
454 // https://streams.spec.whatwg.org/#transform-stream-default-source-pull-algorithm
456 // Step 1: Assert: stream.[[backpressure]] is true.
457 MOZ_ASSERT(mStream
->Backpressure());
459 // Step 2: Assert: stream.[[backpressureChangePromise]] is not undefined.
460 MOZ_ASSERT(mStream
->BackpressureChangePromise());
462 // Step 3: Perform ! TransformStreamSetBackpressure(stream, false).
463 mStream
->SetBackpressure(false);
465 // Step 4: Return stream.[[backpressureChangePromise]].
466 return do_AddRef(mStream
->BackpressureChangePromise());
469 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> CancelCallback(
470 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
471 ErrorResult
& aRv
) override
{
472 // Step 7. Let cancelAlgorithm be the following steps, taking a reason
474 // Step 7.1. Perform ! TransformStreamErrorWritableAndUnblockWrite(stream,
476 TransformStreamErrorWritableAndUnblockWrite(
478 aReason
.WasPassed() ? aReason
.Value() : JS::UndefinedHandleValue
, aRv
);
483 // Step 7.2. Return a promise resolved with undefined.
484 return Promise::CreateResolvedWithUndefined(mStream
->GetParentObject(),
489 ~TransformStreamUnderlyingSourceAlgorithms() override
= default;
492 RefPtr
<Promise
> mStartPromise
;
493 // MOZ_KNOWNLIVE because it will never be reassigned
494 MOZ_KNOWN_LIVE RefPtr
<TransformStream
> mStream
;
497 NS_IMPL_CYCLE_COLLECTION_INHERITED(TransformStreamUnderlyingSourceAlgorithms
,
498 UnderlyingSourceAlgorithmsBase
,
499 mStartPromise
, mStream
)
500 NS_IMPL_ADDREF_INHERITED(TransformStreamUnderlyingSourceAlgorithms
,
501 UnderlyingSourceAlgorithmsBase
)
502 NS_IMPL_RELEASE_INHERITED(TransformStreamUnderlyingSourceAlgorithms
,
503 UnderlyingSourceAlgorithmsBase
)
504 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
505 TransformStreamUnderlyingSourceAlgorithms
)
506 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceAlgorithmsBase
)
508 // https://streams.spec.whatwg.org/#transform-stream-set-backpressure
509 void TransformStream::SetBackpressure(bool aBackpressure
) {
510 // Step 1. Assert: stream.[[backpressure]] is not backpressure.
511 MOZ_ASSERT(Backpressure() != aBackpressure
);
513 // Step 2. If stream.[[backpressureChangePromise]] is not undefined, resolve
514 // stream.[[backpressureChangePromise]] with undefined.
515 if (Promise
* promise
= BackpressureChangePromise()) {
516 promise
->MaybeResolveWithUndefined();
519 // Step 3. Set stream.[[backpressureChangePromise]] to a new promise.
520 RefPtr
<Promise
> promise
= Promise::CreateInfallible(GetParentObject());
521 mBackpressureChangePromise
= promise
;
523 // Step 4. Set stream.[[backpressure]] to backpressure.
524 mBackpressure
= aBackpressure
;
527 // https://streams.spec.whatwg.org/#initialize-transform-stream
528 void TransformStream::Initialize(JSContext
* aCx
, Promise
* aStartPromise
,
529 double aWritableHighWaterMark
,
530 QueuingStrategySize
* aWritableSizeAlgorithm
,
531 double aReadableHighWaterMark
,
532 QueuingStrategySize
* aReadableSizeAlgorithm
,
535 auto sinkAlgorithms
=
536 MakeRefPtr
<TransformStreamUnderlyingSinkAlgorithms
>(aStartPromise
, this);
538 // Step 5. Set stream.[[writable]] to ! CreateWritableStream(startAlgorithm,
539 // writeAlgorithm, closeAlgorithm, abortAlgorithm, writableHighWaterMark,
540 // writableSizeAlgorithm).
542 CreateWritableStream(aCx
, MOZ_KnownLive(mGlobal
), sinkAlgorithms
,
543 aWritableHighWaterMark
, aWritableSizeAlgorithm
, aRv
);
549 auto sourceAlgorithms
= MakeRefPtr
<TransformStreamUnderlyingSourceAlgorithms
>(
550 aStartPromise
, this);
552 // Step 8. Set stream.[[readable]] to ! CreateReadableStream(startAlgorithm,
553 // pullAlgorithm, cancelAlgorithm, readableHighWaterMark,
554 // readableSizeAlgorithm).
555 mReadable
= CreateReadableStream(
556 aCx
, MOZ_KnownLive(mGlobal
), sourceAlgorithms
,
557 Some(aReadableHighWaterMark
), aReadableSizeAlgorithm
, aRv
);
562 // Step 9. Set stream.[[backpressure]] and
563 // stream.[[backpressureChangePromise]] to undefined.
564 // Note(krosylight): The spec allows setting [[backpressure]] as undefined,
565 // but I don't see why it should be. Since the spec also allows strict boolean
566 // type, and this is only to not trigger assertion inside the setter, we just
568 mBackpressure
= false;
569 mBackpressureChangePromise
= nullptr;
571 // Step 10. Perform ! TransformStreamSetBackpressure(stream, true).
572 SetBackpressure(true);
577 // Step 11. Set stream.[[controller]] to undefined.
578 mController
= nullptr;
581 // https://streams.spec.whatwg.org/#ts-constructor
582 already_AddRefed
<TransformStream
> TransformStream::Constructor(
583 const GlobalObject
& aGlobal
,
584 const Optional
<JS::Handle
<JSObject
*>>& aTransformer
,
585 const QueuingStrategy
& aWritableStrategy
,
586 const QueuingStrategy
& aReadableStrategy
, ErrorResult
& aRv
) {
587 // Step 1. If transformer is missing, set it to null.
588 JS::Rooted
<JSObject
*> transformerObj(
590 aTransformer
.WasPassed() ? aTransformer
.Value() : nullptr);
592 // Step 2. Let transformerDict be transformer, converted to an IDL value of
594 RootedDictionary
<Transformer
> transformerDict(aGlobal
.Context());
595 if (transformerObj
) {
596 JS::Rooted
<JS::Value
> objValue(aGlobal
.Context(),
597 JS::ObjectValue(*transformerObj
));
598 dom::BindingCallContext
callCx(aGlobal
.Context(),
599 "TransformStream.constructor");
600 aRv
.MightThrowJSException();
601 if (!transformerDict
.Init(callCx
, objValue
)) {
602 aRv
.StealExceptionFromJSContext(aGlobal
.Context());
607 // Step 3. If transformerDict["readableType"] exists, throw a RangeError
609 if (!transformerDict
.mReadableType
.isUndefined()) {
611 "`readableType` is unsupported and preserved for future use");
615 // Step 4. If transformerDict["writableType"] exists, throw a RangeError
617 if (!transformerDict
.mWritableType
.isUndefined()) {
619 "`writableType` is unsupported and preserved for future use");
623 // Step 5. Let readableHighWaterMark be ?
624 // ExtractHighWaterMark(readableStrategy, 0).
625 double readableHighWaterMark
=
626 ExtractHighWaterMark(aReadableStrategy
, 0, aRv
);
631 // Step 6. Let readableSizeAlgorithm be !
632 // ExtractSizeAlgorithm(readableStrategy).
633 // Note: Callers should recognize nullptr as a callback that returns 1. See
634 // also ReadableStream::Constructor for this design decision.
635 RefPtr
<QueuingStrategySize
> readableSizeAlgorithm
=
636 aReadableStrategy
.mSize
.WasPassed() ? &aReadableStrategy
.mSize
.Value()
639 // Step 7. Let writableHighWaterMark be ?
640 // ExtractHighWaterMark(writableStrategy, 1).
641 double writableHighWaterMark
=
642 ExtractHighWaterMark(aWritableStrategy
, 1, aRv
);
647 // Step 8. Let writableSizeAlgorithm be !
648 // ExtractSizeAlgorithm(writableStrategy).
649 // Note: Callers should recognize nullptr as a callback that returns 1. See
650 // also WritableStream::Constructor for this design decision.
651 RefPtr
<QueuingStrategySize
> writableSizeAlgorithm
=
652 aWritableStrategy
.mSize
.WasPassed() ? &aWritableStrategy
.mSize
.Value()
655 // Step 9. Let startPromise be a new promise.
656 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
657 RefPtr
<Promise
> startPromise
= Promise::CreateInfallible(global
);
659 // Step 10. Perform ! InitializeTransformStream(this, startPromise,
660 // writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark,
661 // readableSizeAlgorithm).
662 RefPtr
<TransformStream
> transformStream
= new TransformStream(global
);
663 transformStream
->Initialize(
664 aGlobal
.Context(), startPromise
, writableHighWaterMark
,
665 writableSizeAlgorithm
, readableHighWaterMark
, readableSizeAlgorithm
, aRv
);
670 // Step 11. Perform ?
671 // SetUpTransformStreamDefaultControllerFromTransformer(this, transformer,
673 SetUpTransformStreamDefaultControllerFromTransformer(
674 aGlobal
.Context(), *transformStream
, transformerObj
, transformerDict
);
676 // Step 12. If transformerDict["start"] exists, then resolve startPromise with
677 // the result of invoking transformerDict["start"] with argument list «
678 // this.[[controller]] » and callback this value transformer.
679 if (transformerDict
.mStart
.WasPassed()) {
680 RefPtr
<TransformerStartCallback
> callback
= transformerDict
.mStart
.Value();
681 RefPtr
<TransformStreamDefaultController
> controller
=
682 transformStream
->Controller();
683 JS::Rooted
<JS::Value
> retVal(aGlobal
.Context());
684 callback
->Call(transformerObj
, *controller
, &retVal
, aRv
,
685 "Transformer.start", CallbackFunction::eRethrowExceptions
);
690 startPromise
->MaybeResolve(retVal
);
692 // Step 13. Otherwise, resolve startPromise with undefined.
693 startPromise
->MaybeResolveWithUndefined();
696 return transformStream
.forget();
699 } // namespace mozilla::dom