Bug 1812348 [wpt PR 38171] - wake lock: Move tests in web_tests/wake-lock to either...
[gecko.git] / dom / streams / TransformStream.cpp
blobb68e2fa89ccdea4ab41d0419382350e2a9bd8591
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.
25 #ifdef __clang__
26 # define MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA MOZ_CAN_RUN_SCRIPT_BOUNDARY
27 #else
28 # define MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA
29 #endif
31 namespace mozilla::dom {
33 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TransformStream, mGlobal,
34 mBackpressureChangePromise, mController,
35 mReadable, mWritable)
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)
41 NS_INTERFACE_MAP_END
43 // https://streams.spec.whatwg.org/#transformstream-set-up
44 // (except this instead creates a new TransformStream rather than accepting an
45 // existing instance)
46 already_AddRefed<TransformStream> TransformStream::CreateGeneric(
47 const GlobalObject& aGlobal, TransformerAlgorithmsWrapper& aAlgorithms,
48 ErrorResult& aRv) {
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);
74 if (!startPromise) {
75 return nullptr;
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);
85 if (aRv.Failed()) {
86 return nullptr;
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,
95 aAlgorithms);
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,
122 ErrorResult& aRv) {
123 // Step 1: Perform !
124 // TransformStreamDefaultControllerClearAlgorithms(stream.[[controller]]).
125 aStream->Controller()->SetAlgorithms(nullptr);
127 // Step 2: Perform !
128 // WritableStreamDefaultControllerErrorIfNeeded(stream.[[writable]].[[controller]],
129 // e).
130 // TODO: Remove MOZ_KnownLive (bug 1761577)
131 WritableStreamDefaultControllerErrorIfNeeded(
132 aCx, MOZ_KnownLive(aStream->Writable()->Controller()), aError, aRv);
133 if (aRv.Failed()) {
134 return;
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) {
147 // Step 1: Perform !
148 // ReadableStreamDefaultControllerError(stream.[[readable]].[[controller]],
149 // e).
150 ReadableStreamDefaultControllerError(
151 aCx, aStream->Readable()->Controller()->AsDefault(), aError, aRv);
152 if (aRv.Failed()) {
153 return;
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);
170 if (aRv.Failed()) {
171 return nullptr;
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]],
181 // r).
182 // TODO: Remove MOZ_KnownLive (bug 1761577)
183 TransformStreamError(aCx, MOZ_KnownLive(aController->Stream()),
184 aError, aRv);
185 if (aRv.Failed()) {
186 return nullptr;
189 // Step 2.2: Throw r.
190 JS::Rooted<JS::Value> r(aCx, aError);
191 aRv.MightThrowJSException();
192 aRv.ThrowJSException(aCx, r);
193 return nullptr;
195 RefPtr(aController));
196 if (result.isErr()) {
197 aRv.Throw(result.unwrapErr());
198 return nullptr;
200 return result.unwrap().forget();
203 // https://streams.spec.whatwg.org/#initialize-transform-stream
204 class TransformStreamUnderlyingSinkAlgorithms final
205 : public UnderlyingSinkAlgorithmsBase {
206 public:
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
228 // argument:
229 // Step 2.1. Return ! TransformStreamDefaultSinkWriteAlgorithm(stream,
230 // chunk).
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);
273 return nullptr;
276 // Step 4: Assert: state is "writable".
277 MOZ_ASSERT(state == WritableStream::WriterState::Writable);
279 // Step 5: Return !
280 // TransformStreamDefaultControllerPerformTransform(controller,
281 // chunk).
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());
289 return nullptr;
291 return result.unwrap().forget();
294 // Step 4: Return !
295 // TransformStreamDefaultControllerPerformTransform(controller, chunk).
296 return TransformStreamDefaultControllerPerformTransform(aCx, controller,
297 aChunk, aRv);
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
304 // argument:
305 // Step 3.1. Return ! TransformStreamDefaultSinkAbortAlgorithm(stream,
306 // reason).
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(
313 aCx, mStream,
314 aReason.WasPassed() ? aReason.Value() : JS::UndefinedHandleValue, aRv);
315 if (aRv.Failed()) {
316 return nullptr;
319 // Step 2: Return a promise resolved with undefined.
320 return Promise::CreateResolvedWithUndefined(mStream->GetParentObject(),
321 aRv);
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);
343 if (aRv.Failed()) {
344 return nullptr;
347 // Step 4: Perform !
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);
369 return nullptr;
372 // Step 5.1.2: Perform !
373 // ReadableStreamDefaultControllerClose(readable.[[controller]]).
374 ReadableStreamDefaultControllerClose(
375 aCx, MOZ_KnownLive(aReadable->Controller()->AsDefault()),
376 aRv);
377 return nullptr;
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);
388 if (aRv.Failed()) {
389 return nullptr;
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);
397 return nullptr;
399 readable, mStream);
401 if (result.isErr()) {
402 aRv.Throw(result.unwrapErr());
403 return nullptr;
405 return result.unwrap().forget();
408 protected:
409 ~TransformStreamUnderlyingSinkAlgorithms() override = default;
411 private:
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,
419 mStream)
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 {
430 public:
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
473 // argument:
474 // Step 7.1. Perform ! TransformStreamErrorWritableAndUnblockWrite(stream,
475 // reason).
476 TransformStreamErrorWritableAndUnblockWrite(
477 aCx, mStream,
478 aReason.WasPassed() ? aReason.Value() : JS::UndefinedHandleValue, aRv);
479 if (aRv.Failed()) {
480 return nullptr;
483 // Step 7.2. Return a promise resolved with undefined.
484 return Promise::CreateResolvedWithUndefined(mStream->GetParentObject(),
485 aRv);
488 protected:
489 ~TransformStreamUnderlyingSourceAlgorithms() override = default;
491 private:
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,
533 ErrorResult& aRv) {
534 // Step 1 - 4
535 auto sinkAlgorithms =
536 MakeRefPtr<TransformStreamUnderlyingSinkAlgorithms>(aStartPromise, this);
538 // Step 5. Set stream.[[writable]] to ! CreateWritableStream(startAlgorithm,
539 // writeAlgorithm, closeAlgorithm, abortAlgorithm, writableHighWaterMark,
540 // writableSizeAlgorithm).
541 mWritable =
542 CreateWritableStream(aCx, MOZ_KnownLive(mGlobal), sinkAlgorithms,
543 aWritableHighWaterMark, aWritableSizeAlgorithm, aRv);
544 if (aRv.Failed()) {
545 return;
548 // Step 6 - 7
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);
558 if (aRv.Failed()) {
559 return;
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
567 // set it as false.
568 mBackpressure = false;
569 mBackpressureChangePromise = nullptr;
571 // Step 10. Perform ! TransformStreamSetBackpressure(stream, true).
572 SetBackpressure(true);
573 if (aRv.Failed()) {
574 return;
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(
589 aGlobal.Context(),
590 aTransformer.WasPassed() ? aTransformer.Value() : nullptr);
592 // Step 2. Let transformerDict be transformer, converted to an IDL value of
593 // type Transformer.
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());
603 return nullptr;
607 // Step 3. If transformerDict["readableType"] exists, throw a RangeError
608 // exception.
609 if (!transformerDict.mReadableType.isUndefined()) {
610 aRv.ThrowRangeError(
611 "`readableType` is unsupported and preserved for future use");
612 return nullptr;
615 // Step 4. If transformerDict["writableType"] exists, throw a RangeError
616 // exception.
617 if (!transformerDict.mWritableType.isUndefined()) {
618 aRv.ThrowRangeError(
619 "`writableType` is unsupported and preserved for future use");
620 return nullptr;
623 // Step 5. Let readableHighWaterMark be ?
624 // ExtractHighWaterMark(readableStrategy, 0).
625 double readableHighWaterMark =
626 ExtractHighWaterMark(aReadableStrategy, 0, aRv);
627 if (aRv.Failed()) {
628 return nullptr;
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()
637 : nullptr;
639 // Step 7. Let writableHighWaterMark be ?
640 // ExtractHighWaterMark(writableStrategy, 1).
641 double writableHighWaterMark =
642 ExtractHighWaterMark(aWritableStrategy, 1, aRv);
643 if (aRv.Failed()) {
644 return nullptr;
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()
653 : nullptr;
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);
666 if (aRv.Failed()) {
667 return nullptr;
670 // Step 11. Perform ?
671 // SetUpTransformStreamDefaultControllerFromTransformer(this, transformer,
672 // transformerDict).
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);
686 if (aRv.Failed()) {
687 return nullptr;
690 startPromise->MaybeResolve(retVal);
691 } else {
692 // Step 13. Otherwise, resolve startPromise with undefined.
693 startPromise->MaybeResolveWithUndefined();
696 return transformStream.forget();
699 } // namespace mozilla::dom