Bug 1857841 - pt 3. Add a new page kind named "fresh" r=glandium
[gecko.git] / dom / streams / TransformStream.cpp
blobe517ecda89a34b27cc1dd0547815389109e0c4d4
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 using namespace streams_abstract;
35 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TransformStream, mGlobal,
36 mBackpressureChangePromise, mController,
37 mReadable, mWritable)
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)
43 NS_INTERFACE_MAP_END
45 // https://streams.spec.whatwg.org/#transformstream-set-up
46 // (except this instead creates a new TransformStream rather than accepting an
47 // existing instance)
48 already_AddRefed<TransformStream> TransformStream::CreateGeneric(
49 const GlobalObject& aGlobal, TransformerAlgorithmsWrapper& aAlgorithms,
50 ErrorResult& aRv) {
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);
76 if (!startPromise) {
77 return nullptr;
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);
88 if (aRv.Failed()) {
89 return nullptr;
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,
98 aAlgorithms);
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,
127 ErrorResult& aRv) {
128 // Step 1: Perform !
129 // TransformStreamDefaultControllerClearAlgorithms(stream.[[controller]]).
130 aStream->Controller()->SetAlgorithms(nullptr);
132 // Step 2: Perform !
133 // WritableStreamDefaultControllerErrorIfNeeded(stream.[[writable]].[[controller]],
134 // e).
135 // TODO: Remove MOZ_KnownLive (bug 1761577)
136 WritableStreamDefaultControllerErrorIfNeeded(
137 aCx, MOZ_KnownLive(aStream->Writable()->Controller()), aError, aRv);
138 if (aRv.Failed()) {
139 return;
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) {
152 // Step 1: Perform !
153 // ReadableStreamDefaultControllerError(stream.[[readable]].[[controller]],
154 // e).
155 ReadableStreamDefaultControllerError(
156 aCx, aStream->Readable()->Controller()->AsDefault(), aError, aRv);
157 if (aRv.Failed()) {
158 return;
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);
177 if (aRv.Failed()) {
178 return nullptr;
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]],
188 // r).
189 // TODO: Remove MOZ_KnownLive (bug 1761577)
190 TransformStreamError(aCx, MOZ_KnownLive(aController->Stream()),
191 aError, aRv);
192 if (aRv.Failed()) {
193 return nullptr;
196 // Step 2.2: Throw r.
197 JS::Rooted<JS::Value> r(aCx, aError);
198 aRv.MightThrowJSException();
199 aRv.ThrowJSException(aCx, r);
200 return nullptr;
202 RefPtr(aController));
203 if (result.isErr()) {
204 aRv.Throw(result.unwrapErr());
205 return nullptr;
207 return result.unwrap().forget();
210 // https://streams.spec.whatwg.org/#initialize-transform-stream
211 class TransformStreamUnderlyingSinkAlgorithms final
212 : public UnderlyingSinkAlgorithmsBase {
213 public:
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
235 // argument:
236 // Step 2.1. Return ! TransformStreamDefaultSinkWriteAlgorithm(stream,
237 // chunk).
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);
280 return nullptr;
283 // Step 4: Assert: state is "writable".
284 MOZ_ASSERT(state == WritableStream::WriterState::Writable);
286 // Step 5: Return !
287 // TransformStreamDefaultControllerPerformTransform(controller,
288 // chunk).
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());
296 return nullptr;
298 return result.unwrap().forget();
301 // Step 4: Return !
302 // TransformStreamDefaultControllerPerformTransform(controller, chunk).
303 return TransformStreamDefaultControllerPerformTransform(aCx, controller,
304 aChunk, aRv);
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
311 // argument:
312 // Step 3.1. Return ! TransformStreamDefaultSinkAbortAlgorithm(stream,
313 // reason).
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(
320 aCx, mStream,
321 aReason.WasPassed() ? aReason.Value() : JS::UndefinedHandleValue, aRv);
322 if (aRv.Failed()) {
323 return nullptr;
326 // Step 2: Return a promise resolved with undefined.
327 return Promise::CreateResolvedWithUndefined(mStream->GetParentObject(),
328 aRv);
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);
350 if (aRv.Failed()) {
351 return nullptr;
354 // Step 4: Perform !
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);
376 return nullptr;
379 // Step 5.1.2: Perform !
380 // ReadableStreamDefaultControllerClose(readable.[[controller]]).
381 ReadableStreamDefaultControllerClose(
382 aCx, MOZ_KnownLive(aReadable->Controller()->AsDefault()),
383 aRv);
384 return nullptr;
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);
395 if (aRv.Failed()) {
396 return nullptr;
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);
404 return nullptr;
406 readable, mStream);
408 if (result.isErr()) {
409 aRv.Throw(result.unwrapErr());
410 return nullptr;
412 return result.unwrap().forget();
415 protected:
416 ~TransformStreamUnderlyingSinkAlgorithms() override = default;
418 private:
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,
426 mStream)
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 {
437 public:
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
480 // argument:
481 // Step 7.1. Perform ! TransformStreamErrorWritableAndUnblockWrite(stream,
482 // reason).
483 TransformStreamErrorWritableAndUnblockWrite(
484 aCx, mStream,
485 aReason.WasPassed() ? aReason.Value() : JS::UndefinedHandleValue, aRv);
486 if (aRv.Failed()) {
487 return nullptr;
490 // Step 7.2. Return a promise resolved with undefined.
491 return Promise::CreateResolvedWithUndefined(mStream->GetParentObject(),
492 aRv);
495 protected:
496 ~TransformStreamUnderlyingSourceAlgorithms() override = default;
498 private:
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,
540 ErrorResult& aRv) {
541 // Step 1 - 4
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);
551 if (aRv.Failed()) {
552 return;
555 // Step 6 - 7
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);
565 if (aRv.Failed()) {
566 return;
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
574 // set it as false.
575 mBackpressure = false;
576 mBackpressureChangePromise = nullptr;
578 // Step 10. Perform ! TransformStreamSetBackpressure(stream, true).
579 SetBackpressure(true);
580 if (aRv.Failed()) {
581 return;
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(
596 aGlobal.Context(),
597 aTransformer.WasPassed() ? aTransformer.Value() : nullptr);
599 // Step 2. Let transformerDict be transformer, converted to an IDL value of
600 // type Transformer.
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());
610 return nullptr;
614 // Step 3. If transformerDict["readableType"] exists, throw a RangeError
615 // exception.
616 if (!transformerDict.mReadableType.isUndefined()) {
617 aRv.ThrowRangeError(
618 "`readableType` is unsupported and preserved for future use");
619 return nullptr;
622 // Step 4. If transformerDict["writableType"] exists, throw a RangeError
623 // exception.
624 if (!transformerDict.mWritableType.isUndefined()) {
625 aRv.ThrowRangeError(
626 "`writableType` is unsupported and preserved for future use");
627 return nullptr;
630 // Step 5. Let readableHighWaterMark be ?
631 // ExtractHighWaterMark(readableStrategy, 0).
632 double readableHighWaterMark =
633 ExtractHighWaterMark(aReadableStrategy, 0, aRv);
634 if (aRv.Failed()) {
635 return nullptr;
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()
644 : nullptr;
646 // Step 7. Let writableHighWaterMark be ?
647 // ExtractHighWaterMark(writableStrategy, 1).
648 double writableHighWaterMark =
649 ExtractHighWaterMark(aWritableStrategy, 1, aRv);
650 if (aRv.Failed()) {
651 return nullptr;
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()
660 : nullptr;
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);
673 if (aRv.Failed()) {
674 return nullptr;
677 // Step 11. Perform ?
678 // SetUpTransformStreamDefaultControllerFromTransformer(this, transformer,
679 // transformerDict).
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);
693 if (aRv.Failed()) {
694 return nullptr;
697 startPromise->MaybeResolve(retVal);
698 } else {
699 // Step 13. Otherwise, resolve startPromise with undefined.
700 startPromise->MaybeResolveWithUndefined();
703 return transformStream.forget();
706 } // namespace mozilla::dom