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 using namespace streams_abstract
;
35 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TransformStream
, mGlobal
,
36 mBackpressureChangePromise
, mController
,
38 NS_IMPL_CYCLE_COLLECTING_ADDREF(TransformStream
)
39 NS_IMPL_CYCLE_COLLECTING_RELEASE(TransformStream
)
40 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStream
)
41 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
42 NS_INTERFACE_MAP_ENTRY(nsISupports
)
45 // https://streams.spec.whatwg.org/#transformstream-set-up
46 // (except this instead creates a new TransformStream rather than accepting an
48 already_AddRefed
<TransformStream
> TransformStream::CreateGeneric(
49 const GlobalObject
& aGlobal
, TransformerAlgorithmsWrapper
& aAlgorithms
,
51 // Step 1. Let writableHighWaterMark be 1.
52 double writableHighWaterMark
= 1;
54 // Step 2. Let writableSizeAlgorithm be an algorithm that returns 1.
55 // Note: Callers should recognize nullptr as a callback that returns 1. See
56 // also WritableStream::Constructor for this design decision.
57 RefPtr
<QueuingStrategySize
> writableSizeAlgorithm
;
59 // Step 3. Let readableHighWaterMark be 0.
60 double readableHighWaterMark
= 0;
62 // Step 4. Let readableSizeAlgorithm be an algorithm that returns 1.
63 // Note: Callers should recognize nullptr as a callback that returns 1. See
64 // also ReadableStream::Constructor for this design decision.
65 RefPtr
<QueuingStrategySize
> readableSizeAlgorithm
;
67 // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps
68 // given a value chunk:
69 // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps:
70 // (Done by TransformerAlgorithmsWrapper)
72 // Step 7. Let startPromise be a promise resolved with undefined.
73 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
74 RefPtr
<Promise
> startPromise
=
75 Promise::CreateResolvedWithUndefined(global
, aRv
);
80 // Step 8. Perform ! InitializeTransformStream(stream, startPromise,
81 // writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark,
82 // readableSizeAlgorithm).
83 RefPtr
<TransformStream
> stream
=
84 new TransformStream(global
, nullptr, nullptr);
85 stream
->Initialize(aGlobal
.Context(), startPromise
, writableHighWaterMark
,
86 writableSizeAlgorithm
, readableHighWaterMark
,
87 readableSizeAlgorithm
, aRv
);
92 // Step 9. Let controller be a new TransformStreamDefaultController.
93 auto controller
= MakeRefPtr
<TransformStreamDefaultController
>(global
);
95 // Step 10. Perform ! SetUpTransformStreamDefaultController(stream,
96 // controller, transformAlgorithmWrapper, flushAlgorithmWrapper).
97 SetUpTransformStreamDefaultController(aGlobal
.Context(), *stream
, *controller
,
100 return stream
.forget();
103 TransformStream::TransformStream(nsIGlobalObject
* aGlobal
) : mGlobal(aGlobal
) {
104 mozilla::HoldJSObjects(this);
107 TransformStream::TransformStream(nsIGlobalObject
* aGlobal
,
108 ReadableStream
* aReadable
,
109 WritableStream
* aWritable
)
110 : mGlobal(aGlobal
), mReadable(aReadable
), mWritable(aWritable
) {
111 mozilla::HoldJSObjects(this);
114 TransformStream::~TransformStream() { mozilla::DropJSObjects(this); }
116 JSObject
* TransformStream::WrapObject(JSContext
* aCx
,
117 JS::Handle
<JSObject
*> aGivenProto
) {
118 return TransformStream_Binding::Wrap(aCx
, this, aGivenProto
);
121 namespace streams_abstract
{
123 // https://streams.spec.whatwg.org/#transform-stream-error-writable-and-unblock-write
124 void TransformStreamErrorWritableAndUnblockWrite(JSContext
* aCx
,
125 TransformStream
* aStream
,
126 JS::Handle
<JS::Value
> aError
,
129 // TransformStreamDefaultControllerClearAlgorithms(stream.[[controller]]).
130 aStream
->Controller()->SetAlgorithms(nullptr);
133 // WritableStreamDefaultControllerErrorIfNeeded(stream.[[writable]].[[controller]],
135 // TODO: Remove MOZ_KnownLive (bug 1761577)
136 WritableStreamDefaultControllerErrorIfNeeded(
137 aCx
, MOZ_KnownLive(aStream
->Writable()->Controller()), aError
, aRv
);
142 // Step 3: If stream.[[backpressure]] is true, perform !
143 // TransformStreamSetBackpressure(stream, false).
144 if (aStream
->Backpressure()) {
145 aStream
->SetBackpressure(false);
149 // https://streams.spec.whatwg.org/#transform-stream-error
150 void TransformStreamError(JSContext
* aCx
, TransformStream
* aStream
,
151 JS::Handle
<JS::Value
> aError
, ErrorResult
& aRv
) {
153 // ReadableStreamDefaultControllerError(stream.[[readable]].[[controller]],
155 ReadableStreamDefaultControllerError(
156 aCx
, aStream
->Readable()->Controller()->AsDefault(), aError
, aRv
);
161 // Step 2: Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, e).
162 TransformStreamErrorWritableAndUnblockWrite(aCx
, aStream
, aError
, aRv
);
165 } // namespace streams_abstract
167 // https://streams.spec.whatwg.org/#transform-stream-default-controller-perform-transform
168 MOZ_CAN_RUN_SCRIPT
static already_AddRefed
<Promise
>
169 TransformStreamDefaultControllerPerformTransform(
170 JSContext
* aCx
, TransformStreamDefaultController
* aController
,
171 JS::Handle
<JS::Value
> aChunk
, ErrorResult
& aRv
) {
172 // Step 1: Let transformPromise be the result of performing
173 // controller.[[transformAlgorithm]], passing chunk.
174 RefPtr
<TransformerAlgorithmsBase
> algorithms
= aController
->Algorithms();
175 RefPtr
<Promise
> transformPromise
=
176 algorithms
->TransformCallback(aCx
, aChunk
, *aController
, aRv
);
181 // Step 2: Return the result of reacting to transformPromise with the
182 // following rejection steps given the argument r:
183 auto result
= transformPromise
->CatchWithCycleCollectedArgs(
184 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aError
, ErrorResult
& aRv
,
185 const RefPtr
<TransformStreamDefaultController
>& aController
)
186 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
-> already_AddRefed
<Promise
> {
187 // Step 2.1: Perform ! TransformStreamError(controller.[[stream]],
189 // TODO: Remove MOZ_KnownLive (bug 1761577)
190 TransformStreamError(aCx
, MOZ_KnownLive(aController
->Stream()),
196 // Step 2.2: Throw r.
197 JS::Rooted
<JS::Value
> r(aCx
, aError
);
198 aRv
.MightThrowJSException();
199 aRv
.ThrowJSException(aCx
, r
);
202 RefPtr(aController
));
203 if (result
.isErr()) {
204 aRv
.Throw(result
.unwrapErr());
207 return result
.unwrap().forget();
210 // https://streams.spec.whatwg.org/#initialize-transform-stream
211 class TransformStreamUnderlyingSinkAlgorithms final
212 : public UnderlyingSinkAlgorithmsBase
{
214 NS_DECL_ISUPPORTS_INHERITED
215 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
216 TransformStreamUnderlyingSinkAlgorithms
, UnderlyingSinkAlgorithmsBase
)
218 TransformStreamUnderlyingSinkAlgorithms(Promise
* aStartPromise
,
219 TransformStream
* aStream
)
220 : mStartPromise(aStartPromise
), mStream(aStream
) {}
222 void StartCallback(JSContext
* aCx
,
223 WritableStreamDefaultController
& aController
,
224 JS::MutableHandle
<JS::Value
> aRetVal
,
225 ErrorResult
& aRv
) override
{
226 // Step 1. Let startAlgorithm be an algorithm that returns startPromise.
227 // (Same as TransformStreamUnderlyingSourceAlgorithms::StartCallback)
228 aRetVal
.setObject(*mStartPromise
->PromiseObj());
231 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> WriteCallback(
232 JSContext
* aCx
, JS::Handle
<JS::Value
> aChunk
,
233 WritableStreamDefaultController
& aController
, ErrorResult
& aRv
) override
{
234 // Step 2. Let writeAlgorithm be the following steps, taking a chunk
236 // Step 2.1. Return ! TransformStreamDefaultSinkWriteAlgorithm(stream,
239 // Inlining TransformStreamDefaultSinkWriteAlgorithm here:
240 // https://streams.spec.whatwg.org/#transform-stream-default-sink-write-algorithm
242 // Step 1: Assert: stream.[[writable]].[[state]] is "writable".
243 MOZ_ASSERT(mStream
->Writable()->State() ==
244 WritableStream::WriterState::Writable
);
246 // Step 2: Let controller be stream.[[controller]].
247 RefPtr
<TransformStreamDefaultController
> controller
= mStream
->Controller();
249 // Step 3: If stream.[[backpressure]] is true,
250 if (mStream
->Backpressure()) {
251 // Step 3.1: Let backpressureChangePromise be
252 // stream.[[backpressureChangePromise]].
253 RefPtr
<Promise
> backpressureChangePromise
=
254 mStream
->BackpressureChangePromise();
256 // Step 3.2: Assert: backpressureChangePromise is not undefined.
257 MOZ_ASSERT(backpressureChangePromise
);
259 // Step 3.3: Return the result of reacting to backpressureChangePromise
260 // with the following fulfillment steps:
261 auto result
= backpressureChangePromise
->ThenWithCycleCollectedArgsJS(
262 [](JSContext
* aCx
, JS::Handle
<JS::Value
>, ErrorResult
& aRv
,
263 const RefPtr
<TransformStream
>& aStream
,
264 const RefPtr
<TransformStreamDefaultController
>& aController
,
265 JS::Handle
<JS::Value
> aChunk
)
266 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
-> already_AddRefed
<Promise
> {
267 // Step 1: Let writable be stream.[[writable]].
268 RefPtr
<WritableStream
> writable
= aStream
->Writable();
270 // Step 2: Let state be writable.[[state]].
271 WritableStream::WriterState state
= writable
->State();
273 // Step 3: If state is "erroring", throw
274 // writable.[[storedError]].
275 if (state
== WritableStream::WriterState::Erroring
) {
276 JS::Rooted
<JS::Value
> storedError(aCx
,
277 writable
->StoredError());
278 aRv
.MightThrowJSException();
279 aRv
.ThrowJSException(aCx
, storedError
);
283 // Step 4: Assert: state is "writable".
284 MOZ_ASSERT(state
== WritableStream::WriterState::Writable
);
287 // TransformStreamDefaultControllerPerformTransform(controller,
289 return TransformStreamDefaultControllerPerformTransform(
290 aCx
, aController
, aChunk
, aRv
);
292 std::make_tuple(mStream
, controller
), std::make_tuple(aChunk
));
294 if (result
.isErr()) {
295 aRv
.Throw(result
.unwrapErr());
298 return result
.unwrap().forget();
302 // TransformStreamDefaultControllerPerformTransform(controller, chunk).
303 return TransformStreamDefaultControllerPerformTransform(aCx
, controller
,
307 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> AbortCallback(
308 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
309 ErrorResult
& aRv
) override
{
310 // Step 3. Let abortAlgorithm be the following steps, taking a reason
312 // Step 3.1. Return ! TransformStreamDefaultSinkAbortAlgorithm(stream,
315 // Inlining TransformStreamDefaultSinkAbortAlgorithm here:
316 // https://streams.spec.whatwg.org/#transform-stream-default-sink-abort-algorithm
318 // Step 1:Perform ! TransformStreamError(stream, reason).
319 TransformStreamError(
321 aReason
.WasPassed() ? aReason
.Value() : JS::UndefinedHandleValue
, aRv
);
326 // Step 2: Return a promise resolved with undefined.
327 return Promise::CreateResolvedWithUndefined(mStream
->GetParentObject(),
331 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> CloseCallback(
332 JSContext
* aCx
, ErrorResult
& aRv
) override
{
333 // Step 4. Let closeAlgorithm be the following steps:
334 // Step 4.1. Return ! TransformStreamDefaultSinkCloseAlgorithm(stream).
336 // Inlining TransformStreamDefaultSinkCloseAlgorithm here:
337 // https://streams.spec.whatwg.org/#transform-stream-default-sink-close-algorithm
339 // Step 1: Let readable be stream.[[readable]].
340 RefPtr
<ReadableStream
> readable
= mStream
->Readable();
342 // Step 2: Let controller be stream.[[controller]].
343 RefPtr
<TransformStreamDefaultController
> controller
= mStream
->Controller();
345 // Step 3: Let flushPromise be the result of performing
346 // controller.[[flushAlgorithm]].
347 RefPtr
<TransformerAlgorithmsBase
> algorithms
= controller
->Algorithms();
348 RefPtr
<Promise
> flushPromise
=
349 algorithms
->FlushCallback(aCx
, *controller
, aRv
);
355 // TransformStreamDefaultControllerClearAlgorithms(controller).
356 controller
->SetAlgorithms(nullptr);
358 // Step 5: Return the result of reacting to flushPromise:
359 Result
<RefPtr
<Promise
>, nsresult
> result
=
360 flushPromise
->ThenCatchWithCycleCollectedArgs(
361 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
,
362 const RefPtr
<ReadableStream
>& aReadable
,
363 const RefPtr
<TransformStream
>& aStream
)
364 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
365 -> already_AddRefed
<Promise
> {
366 // Step 5.1: If flushPromise was fulfilled, then:
368 // Step 5.1.1: If readable.[[state]] is "errored", throw
369 // readable.[[storedError]].
370 if (aReadable
->State() ==
371 ReadableStream::ReaderState::Errored
) {
372 JS::Rooted
<JS::Value
> storedError(aCx
,
373 aReadable
->StoredError());
374 aRv
.MightThrowJSException();
375 aRv
.ThrowJSException(aCx
, storedError
);
379 // Step 5.1.2: Perform !
380 // ReadableStreamDefaultControllerClose(readable.[[controller]]).
381 ReadableStreamDefaultControllerClose(
382 aCx
, MOZ_KnownLive(aReadable
->Controller()->AsDefault()),
386 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
,
387 const RefPtr
<ReadableStream
>& aReadable
,
388 const RefPtr
<TransformStream
>& aStream
)
389 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
390 -> already_AddRefed
<Promise
> {
391 // Step 5.2: If flushPromise was rejected with reason r, then:
393 // Step 5.2.1: Perform ! TransformStreamError(stream, r).
394 TransformStreamError(aCx
, aStream
, aValue
, aRv
);
399 // Step 5.2.2: Throw readable.[[storedError]].
400 JS::Rooted
<JS::Value
> storedError(aCx
,
401 aReadable
->StoredError());
402 aRv
.MightThrowJSException();
403 aRv
.ThrowJSException(aCx
, storedError
);
408 if (result
.isErr()) {
409 aRv
.Throw(result
.unwrapErr());
412 return result
.unwrap().forget();
416 ~TransformStreamUnderlyingSinkAlgorithms() override
= default;
419 RefPtr
<Promise
> mStartPromise
;
420 // MOZ_KNOWN_LIVE because it won't be reassigned
421 MOZ_KNOWN_LIVE RefPtr
<TransformStream
> mStream
;
424 NS_IMPL_CYCLE_COLLECTION_INHERITED(TransformStreamUnderlyingSinkAlgorithms
,
425 UnderlyingSinkAlgorithmsBase
, mStartPromise
,
427 NS_IMPL_ADDREF_INHERITED(TransformStreamUnderlyingSinkAlgorithms
,
428 UnderlyingSinkAlgorithmsBase
)
429 NS_IMPL_RELEASE_INHERITED(TransformStreamUnderlyingSinkAlgorithms
,
430 UnderlyingSinkAlgorithmsBase
)
431 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransformStreamUnderlyingSinkAlgorithms
)
432 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSinkAlgorithmsBase
)
434 // https://streams.spec.whatwg.org/#initialize-transform-stream
435 class TransformStreamUnderlyingSourceAlgorithms final
436 : public UnderlyingSourceAlgorithmsBase
{
438 NS_DECL_ISUPPORTS_INHERITED
439 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(
440 TransformStreamUnderlyingSourceAlgorithms
, UnderlyingSourceAlgorithmsBase
)
442 TransformStreamUnderlyingSourceAlgorithms(Promise
* aStartPromise
,
443 TransformStream
* aStream
)
444 : mStartPromise(aStartPromise
), mStream(aStream
) {}
446 void StartCallback(JSContext
* aCx
, ReadableStreamController
& aController
,
447 JS::MutableHandle
<JS::Value
> aRetVal
,
448 ErrorResult
& aRv
) override
{
449 // Step 1. Let startAlgorithm be an algorithm that returns startPromise.
450 // (Same as TransformStreamUnderlyingSinkAlgorithms::StartCallback)
451 aRetVal
.setObject(*mStartPromise
->PromiseObj());
454 already_AddRefed
<Promise
> PullCallback(JSContext
* aCx
,
455 ReadableStreamController
& aController
,
456 ErrorResult
& aRv
) override
{
457 // Step 6. Let pullAlgorithm be the following steps:
458 // Step 6.1. Return ! TransformStreamDefaultSourcePullAlgorithm(stream).
460 // Inlining TransformStreamDefaultSourcePullAlgorithm here:
461 // https://streams.spec.whatwg.org/#transform-stream-default-source-pull-algorithm
463 // Step 1: Assert: stream.[[backpressure]] is true.
464 MOZ_ASSERT(mStream
->Backpressure());
466 // Step 2: Assert: stream.[[backpressureChangePromise]] is not undefined.
467 MOZ_ASSERT(mStream
->BackpressureChangePromise());
469 // Step 3: Perform ! TransformStreamSetBackpressure(stream, false).
470 mStream
->SetBackpressure(false);
472 // Step 4: Return stream.[[backpressureChangePromise]].
473 return do_AddRef(mStream
->BackpressureChangePromise());
476 MOZ_CAN_RUN_SCRIPT already_AddRefed
<Promise
> CancelCallback(
477 JSContext
* aCx
, const Optional
<JS::Handle
<JS::Value
>>& aReason
,
478 ErrorResult
& aRv
) override
{
479 // Step 7. Let cancelAlgorithm be the following steps, taking a reason
481 // Step 7.1. Perform ! TransformStreamErrorWritableAndUnblockWrite(stream,
483 TransformStreamErrorWritableAndUnblockWrite(
485 aReason
.WasPassed() ? aReason
.Value() : JS::UndefinedHandleValue
, aRv
);
490 // Step 7.2. Return a promise resolved with undefined.
491 return Promise::CreateResolvedWithUndefined(mStream
->GetParentObject(),
496 ~TransformStreamUnderlyingSourceAlgorithms() override
= default;
499 RefPtr
<Promise
> mStartPromise
;
500 // MOZ_KNOWNLIVE because it will never be reassigned
501 MOZ_KNOWN_LIVE RefPtr
<TransformStream
> mStream
;
504 NS_IMPL_CYCLE_COLLECTION_INHERITED(TransformStreamUnderlyingSourceAlgorithms
,
505 UnderlyingSourceAlgorithmsBase
,
506 mStartPromise
, mStream
)
507 NS_IMPL_ADDREF_INHERITED(TransformStreamUnderlyingSourceAlgorithms
,
508 UnderlyingSourceAlgorithmsBase
)
509 NS_IMPL_RELEASE_INHERITED(TransformStreamUnderlyingSourceAlgorithms
,
510 UnderlyingSourceAlgorithmsBase
)
511 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
512 TransformStreamUnderlyingSourceAlgorithms
)
513 NS_INTERFACE_MAP_END_INHERITING(UnderlyingSourceAlgorithmsBase
)
515 // https://streams.spec.whatwg.org/#transform-stream-set-backpressure
516 void TransformStream::SetBackpressure(bool aBackpressure
) {
517 // Step 1. Assert: stream.[[backpressure]] is not backpressure.
518 MOZ_ASSERT(Backpressure() != aBackpressure
);
520 // Step 2. If stream.[[backpressureChangePromise]] is not undefined, resolve
521 // stream.[[backpressureChangePromise]] with undefined.
522 if (Promise
* promise
= BackpressureChangePromise()) {
523 promise
->MaybeResolveWithUndefined();
526 // Step 3. Set stream.[[backpressureChangePromise]] to a new promise.
527 RefPtr
<Promise
> promise
= Promise::CreateInfallible(GetParentObject());
528 mBackpressureChangePromise
= promise
;
530 // Step 4. Set stream.[[backpressure]] to backpressure.
531 mBackpressure
= aBackpressure
;
534 // https://streams.spec.whatwg.org/#initialize-transform-stream
535 void TransformStream::Initialize(JSContext
* aCx
, Promise
* aStartPromise
,
536 double aWritableHighWaterMark
,
537 QueuingStrategySize
* aWritableSizeAlgorithm
,
538 double aReadableHighWaterMark
,
539 QueuingStrategySize
* aReadableSizeAlgorithm
,
542 auto sinkAlgorithms
=
543 MakeRefPtr
<TransformStreamUnderlyingSinkAlgorithms
>(aStartPromise
, this);
545 // Step 5. Set stream.[[writable]] to ! CreateWritableStream(startAlgorithm,
546 // writeAlgorithm, closeAlgorithm, abortAlgorithm, writableHighWaterMark,
547 // writableSizeAlgorithm).
548 mWritable
= WritableStream::CreateAbstract(
549 aCx
, MOZ_KnownLive(mGlobal
), sinkAlgorithms
, aWritableHighWaterMark
,
550 aWritableSizeAlgorithm
, aRv
);
556 auto sourceAlgorithms
= MakeRefPtr
<TransformStreamUnderlyingSourceAlgorithms
>(
557 aStartPromise
, this);
559 // Step 8. Set stream.[[readable]] to ! CreateReadableStream(startAlgorithm,
560 // pullAlgorithm, cancelAlgorithm, readableHighWaterMark,
561 // readableSizeAlgorithm).
562 mReadable
= ReadableStream::CreateAbstract(
563 aCx
, MOZ_KnownLive(mGlobal
), sourceAlgorithms
,
564 Some(aReadableHighWaterMark
), aReadableSizeAlgorithm
, aRv
);
569 // Step 9. Set stream.[[backpressure]] and
570 // stream.[[backpressureChangePromise]] to undefined.
571 // Note(krosylight): The spec allows setting [[backpressure]] as undefined,
572 // but I don't see why it should be. Since the spec also allows strict boolean
573 // type, and this is only to not trigger assertion inside the setter, we just
575 mBackpressure
= false;
576 mBackpressureChangePromise
= nullptr;
578 // Step 10. Perform ! TransformStreamSetBackpressure(stream, true).
579 SetBackpressure(true);
584 // Step 11. Set stream.[[controller]] to undefined.
585 mController
= nullptr;
588 // https://streams.spec.whatwg.org/#ts-constructor
589 already_AddRefed
<TransformStream
> TransformStream::Constructor(
590 const GlobalObject
& aGlobal
,
591 const Optional
<JS::Handle
<JSObject
*>>& aTransformer
,
592 const QueuingStrategy
& aWritableStrategy
,
593 const QueuingStrategy
& aReadableStrategy
, ErrorResult
& aRv
) {
594 // Step 1. If transformer is missing, set it to null.
595 JS::Rooted
<JSObject
*> transformerObj(
597 aTransformer
.WasPassed() ? aTransformer
.Value() : nullptr);
599 // Step 2. Let transformerDict be transformer, converted to an IDL value of
601 RootedDictionary
<Transformer
> transformerDict(aGlobal
.Context());
602 if (transformerObj
) {
603 JS::Rooted
<JS::Value
> objValue(aGlobal
.Context(),
604 JS::ObjectValue(*transformerObj
));
605 dom::BindingCallContext
callCx(aGlobal
.Context(),
606 "TransformStream.constructor");
607 aRv
.MightThrowJSException();
608 if (!transformerDict
.Init(callCx
, objValue
)) {
609 aRv
.StealExceptionFromJSContext(aGlobal
.Context());
614 // Step 3. If transformerDict["readableType"] exists, throw a RangeError
616 if (!transformerDict
.mReadableType
.isUndefined()) {
618 "`readableType` is unsupported and preserved for future use");
622 // Step 4. If transformerDict["writableType"] exists, throw a RangeError
624 if (!transformerDict
.mWritableType
.isUndefined()) {
626 "`writableType` is unsupported and preserved for future use");
630 // Step 5. Let readableHighWaterMark be ?
631 // ExtractHighWaterMark(readableStrategy, 0).
632 double readableHighWaterMark
=
633 ExtractHighWaterMark(aReadableStrategy
, 0, aRv
);
638 // Step 6. Let readableSizeAlgorithm be !
639 // ExtractSizeAlgorithm(readableStrategy).
640 // Note: Callers should recognize nullptr as a callback that returns 1. See
641 // also ReadableStream::Constructor for this design decision.
642 RefPtr
<QueuingStrategySize
> readableSizeAlgorithm
=
643 aReadableStrategy
.mSize
.WasPassed() ? &aReadableStrategy
.mSize
.Value()
646 // Step 7. Let writableHighWaterMark be ?
647 // ExtractHighWaterMark(writableStrategy, 1).
648 double writableHighWaterMark
=
649 ExtractHighWaterMark(aWritableStrategy
, 1, aRv
);
654 // Step 8. Let writableSizeAlgorithm be !
655 // ExtractSizeAlgorithm(writableStrategy).
656 // Note: Callers should recognize nullptr as a callback that returns 1. See
657 // also WritableStream::Constructor for this design decision.
658 RefPtr
<QueuingStrategySize
> writableSizeAlgorithm
=
659 aWritableStrategy
.mSize
.WasPassed() ? &aWritableStrategy
.mSize
.Value()
662 // Step 9. Let startPromise be a new promise.
663 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
664 RefPtr
<Promise
> startPromise
= Promise::CreateInfallible(global
);
666 // Step 10. Perform ! InitializeTransformStream(this, startPromise,
667 // writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark,
668 // readableSizeAlgorithm).
669 RefPtr
<TransformStream
> transformStream
= new TransformStream(global
);
670 transformStream
->Initialize(
671 aGlobal
.Context(), startPromise
, writableHighWaterMark
,
672 writableSizeAlgorithm
, readableHighWaterMark
, readableSizeAlgorithm
, aRv
);
677 // Step 11. Perform ?
678 // SetUpTransformStreamDefaultControllerFromTransformer(this, transformer,
680 SetUpTransformStreamDefaultControllerFromTransformer(
681 aGlobal
.Context(), *transformStream
, transformerObj
, transformerDict
);
683 // Step 12. If transformerDict["start"] exists, then resolve startPromise with
684 // the result of invoking transformerDict["start"] with argument list «
685 // this.[[controller]] » and callback this value transformer.
686 if (transformerDict
.mStart
.WasPassed()) {
687 RefPtr
<TransformerStartCallback
> callback
= transformerDict
.mStart
.Value();
688 RefPtr
<TransformStreamDefaultController
> controller
=
689 transformStream
->Controller();
690 JS::Rooted
<JS::Value
> retVal(aGlobal
.Context());
691 callback
->Call(transformerObj
, *controller
, &retVal
, aRv
,
692 "Transformer.start", CallbackFunction::eRethrowExceptions
);
697 startPromise
->MaybeResolve(retVal
);
699 // Step 13. Otherwise, resolve startPromise with undefined.
700 startPromise
->MaybeResolveWithUndefined();
703 return transformStream
.forget();
706 } // namespace mozilla::dom