Skip to main content

script/dom/stream/
transformstreamdefaultcontroller.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::RefCell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::jsapi::{
11    ExceptionStackBehavior, Heap, JS_IsExceptionPending, JS_SetPendingException, JSObject,
12};
13use js::jsval::UndefinedValue;
14use js::realm::CurrentRealm;
15use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue};
16use script_bindings::cell::DomRefCell;
17use script_bindings::reflector::{Reflector, reflect_dom_object};
18
19use crate::dom::bindings::callback::{ExceptionHandling, TracedCallback};
20use crate::dom::bindings::codegen::Bindings::TransformStreamDefaultControllerBinding::TransformStreamDefaultControllerMethods;
21use crate::dom::bindings::codegen::Bindings::TransformerBinding::{
22    Transformer, TransformerCancelCallback, TransformerFlushCallback, TransformerTransformCallback,
23};
24use crate::dom::bindings::error::{Error, ErrorToJsval, Fallible};
25use crate::dom::bindings::reflector::DomGlobal;
26use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
27use crate::dom::compressionstream::{
28    CompressionStream, compress_and_enqueue_a_chunk, compress_flush_and_enqueue,
29};
30use crate::dom::decompressionstream::{
31    decompress_and_enqueue_a_chunk, decompress_flush_and_enqueue,
32};
33use crate::dom::encoding::textdecodercommon::TextDecoderCommon;
34use crate::dom::encoding::textdecoderstream::{decode_and_enqueue_a_chunk, flush_and_enqueue};
35use crate::dom::encoding::textencoderstream::{
36    Encoder, encode_and_enqueue_a_chunk, encode_and_flush,
37};
38use crate::dom::globalscope::GlobalScope;
39use crate::dom::promise::{Promise, RootedPromise, TracedPromise};
40use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
41use crate::dom::types::{DecompressionStream, TransformStream};
42use crate::realms::enter_auto_realm;
43
44impl js::gc::Rootable for TransformTransformPromiseRejection {}
45
46/// Reacting to transformPromise as part of
47/// <https://streams.spec.whatwg.org/#transform-stream-default-controller-perform-transform>
48#[derive(JSTraceable, MallocSizeOf)]
49#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
50struct TransformTransformPromiseRejection {
51    controller: Dom<TransformStreamDefaultController>,
52}
53
54impl Callback for TransformTransformPromiseRejection {
55    /// Reacting to transformPromise with the following fulfillment steps:
56    fn callback(&self, cx: &mut CurrentRealm, v: SafeHandleValue) {
57        // Perform ! TransformStreamError(controller.[[stream]], r).
58        self.controller.error(cx, &self.controller.global(), v);
59
60        // Throw r.
61        // Note: this is done part of perform_transform().
62    }
63}
64
65/// The type of transformer algorithms we are using
66#[derive(JSTraceable, MallocSizeOf)]
67#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
68pub(crate) enum TransformerType {
69    /// Algorithms provided by Js callbacks
70    Js {
71        /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-cancelalgorithm>
72        cancel: RefCell<Option<TracedCallback<TransformerCancelCallback>>>,
73
74        /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-flushalgorithm>
75        flush: RefCell<Option<TracedCallback<TransformerFlushCallback>>>,
76
77        /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-transformalgorithm>
78        transform: RefCell<Option<TracedCallback<TransformerTransformCallback>>>,
79
80        /// The JS object used as `this` when invoking sink algorithms.
81        #[ignore_malloc_size_of = "mozjs"]
82        transform_obj: Heap<*mut JSObject>,
83    },
84    /// Algorithms supporting `TextDecoderStream` are implemented in Rust
85    ///
86    /// <https://encoding.spec.whatwg.org/#textdecodercommon>
87    Decoder(#[conditional_malloc_size_of] Rc<TextDecoderCommon>),
88    /// Algorithms supporting `TextEncoderStream` are implemented in Rust
89    ///
90    /// <https://encoding.spec.whatwg.org/#textencoderstream-encoder>
91    Encoder(Encoder),
92    /// Algorithms supporting `CompressionStream` are implemented in Rust
93    ///
94    /// <https://compression.spec.whatwg.org/#compressionstream>
95    Compressor(Dom<CompressionStream>),
96    /// Algorithms supporting `DecompressionStream` are implemented in Rust
97    ///
98    /// <https://compression.spec.whatwg.org/#decompressionstream>
99    Decompressor(Dom<DecompressionStream>),
100}
101
102impl TransformerType {
103    pub(crate) fn new_from_js_transformer(transformer: &Transformer) -> TransformerType {
104        TransformerType::Js {
105            cancel: RefCell::new(transformer.cancel.clone()),
106            flush: RefCell::new(transformer.flush.clone()),
107            transform: RefCell::new(transformer.transform.clone()),
108            transform_obj: Default::default(),
109        }
110    }
111}
112
113/// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller>
114#[dom_struct]
115pub struct TransformStreamDefaultController {
116    reflector_: Reflector,
117
118    /// The type of the underlying transformer used. Besides the JS variant,
119    /// there will be other variant(s) for `TextDecoderStream`
120    transformer_type: TransformerType,
121
122    /// <https://streams.spec.whatwg.org/#TransformStreamDefaultController-stream>
123    stream: MutNullableDom<TransformStream>,
124
125    /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-finishpromise>
126    finish_promise: DomRefCell<Option<TracedPromise>>,
127}
128
129impl TransformStreamDefaultController {
130    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
131    fn new_inherited(transformer_type: TransformerType) -> TransformStreamDefaultController {
132        TransformStreamDefaultController {
133            reflector_: Reflector::new(),
134            transformer_type,
135            stream: MutNullableDom::new(None),
136            finish_promise: DomRefCell::new(None),
137        }
138    }
139
140    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
141    pub(crate) fn new(
142        cx: &mut JSContext,
143        global: &GlobalScope,
144        transformer_type: TransformerType,
145    ) -> DomRoot<TransformStreamDefaultController> {
146        reflect_dom_object(
147            cx,
148            Box::new(TransformStreamDefaultController::new_inherited(
149                transformer_type,
150            )),
151            global,
152        )
153    }
154
155    /// Setting the JS object after the heap has settled down.
156    ///
157    /// Note that this has no effect if the transformer type is not `TransformerType::Js`
158    pub(crate) fn set_transform_obj(&self, this_object: SafeHandleObject) {
159        if let TransformerType::Js { transform_obj, .. } = &self.transformer_type {
160            transform_obj.set(*this_object)
161        } else {
162            unreachable!("Non-Js transformer type should not set transform_obj")
163        }
164    }
165
166    pub(crate) fn set_stream(&self, stream: &TransformStream) {
167        self.stream.set(Some(stream));
168    }
169
170    pub(crate) fn get_finish_promise(&self, cx: &JSContext) -> Option<RootedPromise> {
171        self.finish_promise
172            .borrow()
173            .as_ref()
174            .map(|promise| promise.root(cx))
175    }
176
177    pub(crate) fn set_finish_promise(&self, promise: &RootedPromise) {
178        *self.finish_promise.borrow_mut() = Some(promise.to_traced());
179    }
180
181    /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-perform-transform>
182    pub(crate) fn transform_stream_default_controller_perform_transform(
183        &self,
184        cx: &mut JSContext,
185        global: &GlobalScope,
186        chunk: SafeHandleValue,
187    ) -> Fallible<RootedPromise> {
188        // Let transformPromise be the result of performing controller.[[transformAlgorithm]], passing chunk.
189        let transform_promise = self.perform_transform(cx, global, chunk)?;
190
191        // Return the result of reacting to transformPromise with the following rejection steps given the argument r:
192        rooted!(&in(cx) let mut reject_handler = Some(TransformTransformPromiseRejection {
193            controller: Dom::from_ref(self),
194        }));
195
196        let handler = PromiseNativeHandler::new(
197            cx,
198            global,
199            None,
200            reject_handler.take().map(|h| Box::new(h) as Box<_>),
201        );
202        let mut realm = enter_auto_realm(cx, global);
203        let realm = &mut realm.current_realm();
204        transform_promise.append_native_handler(realm, &handler);
205
206        Ok(transform_promise)
207    }
208
209    pub(crate) fn perform_transform(
210        &self,
211        cx: &mut JSContext,
212        global: &GlobalScope,
213        chunk: SafeHandleValue,
214    ) -> Fallible<RootedPromise> {
215        let result = match &self.transformer_type {
216            // <https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer>
217            TransformerType::Js {
218                transform,
219                transform_obj,
220                ..
221            } => {
222                // Step 5. If transformerDict["transform"] exists, set
223                // transformAlgorithm to an algorithm which takes an argument
224                // chunk and returns the result of invoking
225                // transformerDict["transform"] with argument list « chunk,
226                // controller » and callback this value transformer.
227                rooted!(&in(cx) let algo = transform.borrow().clone());
228                if let Some(ref transform) = *algo {
229                    rooted!(&in(cx) let this_object = transform_obj.get());
230                    transform
231                        .Call_(
232                            cx,
233                            &this_object.handle(),
234                            chunk,
235                            self,
236                            ExceptionHandling::Rethrow,
237                        )
238                        .unwrap_or_else(|e| {
239                            let p = Promise::new(cx, global);
240                            p.reject_error(cx, e);
241                            p
242                        })
243                } else {
244                    // Step 2. Let transformAlgorithm be the following steps, taking a chunk argument:
245                    // Let result be TransformStreamDefaultControllerEnqueue(controller, chunk).
246                    // If result is an abrupt completion, return a promise rejected with result.[[Value]].
247                    if let Err(error) = self.enqueue(cx, global, chunk) {
248                        rooted!(&in(cx) let mut error_val = UndefinedValue());
249                        error.to_jsval(cx, global, error_val.handle_mut());
250                        Promise::new_rejected(cx, global, error_val.handle())
251                    } else {
252                        // Otherwise, return a promise resolved with undefined.
253                        Promise::new_resolved(cx, global, ())
254                    }
255                }
256            },
257            TransformerType::Decoder(decoder) => {
258                // <https://encoding.spec.whatwg.org/#dom-textdecoderstream>
259                // Step 7. Let transformAlgorithm be an algorithm which takes a
260                // chunk argument and runs the decode and enqueue a chunk
261                // algorithm with this and chunk.
262                decode_and_enqueue_a_chunk(cx, global, chunk, decoder, self)
263                    // <https://streams.spec.whatwg.org/#transformstream-set-up>
264                    // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps given a value chunk:
265                    // Step 5.1 Let result be the result of running transformAlgorithm given chunk.
266                    // Step 5.2 If result is a Promise, then return result.
267                    // Note: not applicable, the spec does NOT require deode_and_enqueue_a_chunk() to return a Promise
268                    // Step 5.3 Return a promise resolved with undefined.
269                    .map(|_| Promise::new_resolved(cx, global, ()))
270                    .unwrap_or_else(|e| {
271                        // <https://streams.spec.whatwg.org/#transformstream-set-up>
272                        // Step 5.1 If this throws an exception e,
273                        let mut realm = enter_auto_realm(cx, self);
274                        let realm = &mut realm.current_realm();
275                        let p = Promise::new_in_realm(realm);
276                        // return a promise rejected with e.
277                        p.reject_error(realm, e);
278                        p
279                    })
280            },
281            TransformerType::Encoder(encoder) => {
282                // <https://encoding.spec.whatwg.org/#dom-textencoderstream>
283                // Step 2. Let transformAlgorithm be an algorithm which takes a chunk argument and runs the encode
284                //      and enqueue a chunk algorithm with this and chunk.
285                encode_and_enqueue_a_chunk(cx, global, chunk, encoder, self)
286                    // <https://streams.spec.whatwg.org/#transformstream-set-up>
287                    // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps given a value chunk:
288                    // Step 5.1 Let result be the result of running transformAlgorithm given chunk.
289                    // Step 5.2 If result is a Promise, then return result.
290                    // Note: not applicable, the spec does NOT require encode_and_enqueue_a_chunk() to return a Promise
291                    // Step 5.3 Return a promise resolved with undefined.
292                    .map(|_| Promise::new_resolved(cx, global, ()))
293                    .unwrap_or_else(|e| {
294                        // <https://streams.spec.whatwg.org/#transformstream-set-up>
295                        // Step 5.1 If this throws an exception e,
296                        let mut realm = enter_auto_realm(cx, self);
297                        let realm = &mut realm.current_realm();
298                        let p = Promise::new_in_realm(realm);
299                        // return a promise rejected with e.
300                        p.reject_error(realm, e);
301                        p
302                    })
303            },
304            TransformerType::Compressor(cs) => {
305                // <https://compression.spec.whatwg.org/#dom-compressionstream-compressionstream>
306                // Step 3. Let transformAlgorithm be an algorithm which takes a chunk argument and
307                // runs the compress and enqueue a chunk algorithm with this and chunk.
308                compress_and_enqueue_a_chunk(cx, global, cs, chunk, self)
309                    // <https://streams.spec.whatwg.org/#transformstream-set-up>
310                    // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps given a value chunk:
311                    // Step 5.1 Let result be the result of running transformAlgorithm given chunk.
312                    // Step 5.2 If result is a Promise, then return result.
313                    // Note: not applicable, the spec does NOT require
314                    // compress_and_enqueue_a_chunk() to return a Promise.
315                    // Step 5.3 Return a promise resolved with undefined.
316                    .map(|_| Promise::new_resolved(cx, global, ()))
317                    .unwrap_or_else(|e| {
318                        // <https://streams.spec.whatwg.org/#transformstream-set-up>
319                        // Step 5.1 If this throws an exception e,
320                        let mut realm = enter_auto_realm(cx, self);
321                        let realm = &mut realm.current_realm();
322                        let p = Promise::new_in_realm(realm);
323                        // return a promise rejected with e.
324                        p.reject_error(realm, e);
325                        p
326                    })
327            },
328            TransformerType::Decompressor(ds) => {
329                // <https://compression.spec.whatwg.org/#dom-decompressionstream-decompressionstream>
330                // Step 3. Let transformAlgorithm be an algorithm which takes a chunk argument and
331                // runs the decompress and enqueue a chunk algorithm with this and chunk.
332                decompress_and_enqueue_a_chunk(cx, global, ds, chunk, self)
333                    // <https://streams.spec.whatwg.org/#transformstream-set-up>
334                    // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps given a value chunk:
335                    // Step 5.1 Let result be the result of running transformAlgorithm given chunk.
336                    // Step 5.2 If result is a Promise, then return result.
337                    // Note: not applicable, the spec does NOT require
338                    // decompress_and_enqueue_a_chunk() to return a Promise
339                    // Step 5.3 Return a promise resolved with undefined.
340                    .map(|_| Promise::new_resolved(cx, global, ()))
341                    .unwrap_or_else(|e| {
342                        // <https://streams.spec.whatwg.org/#transformstream-set-up>
343                        // Step 5.1 If this throws an exception e,
344                        let mut realm = enter_auto_realm(cx, self);
345                        let realm = &mut realm.current_realm();
346                        let p = Promise::new_in_realm(realm);
347                        // return a promise rejected with e.
348                        p.reject_error(realm, e);
349                        p
350                    })
351            },
352        };
353
354        Ok(result)
355    }
356
357    pub(crate) fn perform_cancel(
358        &self,
359        cx: &mut JSContext,
360        global: &GlobalScope,
361        chunk: SafeHandleValue,
362    ) -> Fallible<RootedPromise> {
363        let result = match &self.transformer_type {
364            // <https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer>
365            TransformerType::Js {
366                cancel,
367                transform_obj,
368                ..
369            } => {
370                // Step 7. If transformerDict["cancel"] exists, set
371                // cancelAlgorithm to an algorithm which takes an argument
372                // reason and returns the result of invoking
373                // transformerDict["cancel"] with argument list « reason » and
374                // callback this value transformer.
375                rooted!(&in(cx) let algo = cancel.borrow().clone());
376                if let Some(ref cancel) = *algo {
377                    rooted!(&in(cx) let this_object = transform_obj.get());
378                    cancel
379                        .Call_(cx, &this_object.handle(), chunk, ExceptionHandling::Rethrow)
380                        .unwrap_or_else(|e| {
381                            let p = Promise::new(cx, global);
382                            p.reject_error(cx, e);
383                            p
384                        })
385                } else {
386                    // Step 4. Let cancelAlgorithm be an algorithm which returns a promise resolved with undefined.
387                    Promise::new_resolved(cx, global, ())
388                }
389            },
390            TransformerType::Decoder(_) => {
391                // <https://streams.spec.whatwg.org/#transformstream-set-up>
392                // Step 7. Let cancelAlgorithmWrapper be an algorithm that runs these steps given a value reason:
393                // Step 7.1 Let result be the result of running cancelAlgorithm given reason,
394                //      if cancelAlgorithm was given, or null otherwise
395                // Note: `TextDecoderStream` does NOT specify a cancel algorithm.
396                // Step 7.2 If result is a Promise, then return result.
397                // Note: Not applicable.
398                // Step 7.3 Return a promise resolved with undefined.
399                Promise::new_resolved(cx, global, ())
400            },
401            TransformerType::Encoder(_) => {
402                // <https://streams.spec.whatwg.org/#transformstream-set-up>
403                // Step 7. Let cancelAlgorithmWrapper be an algorithm that runs these steps given a value reason:
404                // Step 7.1 Let result be the result of running cancelAlgorithm given reason,
405                //      if cancelAlgorithm was given, or null otherwise
406                // Note: `TextDecoderStream` does NOT specify a cancel algorithm.
407                // Step 7.2 If result is a Promise, then return result.
408                // Note: Not applicable.
409                // Step 7.3 Return a promise resolved with undefined.
410                Promise::new_resolved(cx, global, ())
411            },
412            TransformerType::Compressor(_) => {
413                // <https://streams.spec.whatwg.org/#transformstream-set-up>
414                // Step 7. Let cancelAlgorithmWrapper be an algorithm that runs these steps given a value reason:
415                // Step 7.1 Let result be the result of running cancelAlgorithm given reason,
416                //      if cancelAlgorithm was given, or null otherwise
417                // Note: `CompressionStream` does NOT specify a cancel algorithm.
418                // Step 7.2 If result is a Promise, then return result.
419                // Note: Not applicable.
420                // Step 7.3 Return a promise resolved with undefined.
421                Promise::new_resolved(cx, global, ())
422            },
423            TransformerType::Decompressor(_) => {
424                // <https://streams.spec.whatwg.org/#transformstream-set-up>
425                // Step 7. Let cancelAlgorithmWrapper be an algorithm that runs these steps given a value reason:
426                // Step 7.1 Let result be the result of running cancelAlgorithm given reason,
427                //      if cancelAlgorithm was given, or null otherwise
428                // Note: `DecompressionStream` does NOT specify a cancel algorithm.
429                // Step 7.2 If result is a Promise, then return result.
430                // Note: Not applicable.
431                // Step 7.3 Return a promise resolved with undefined.
432                Promise::new_resolved(cx, global, ())
433            },
434        };
435
436        Ok(result)
437    }
438
439    pub(crate) fn perform_flush(
440        &self,
441        cx: &mut JSContext,
442        global: &GlobalScope,
443    ) -> Fallible<RootedPromise> {
444        let result = match &self.transformer_type {
445            // <https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer>
446            TransformerType::Js {
447                flush,
448                transform_obj,
449                ..
450            } => {
451                // Step 6. If transformerDict["flush"] exists, set flushAlgorithm to an
452                // algorithm which returns the result of invoking
453                // transformerDict["flush"] with argument list « controller »
454                // and callback this value transformer.
455                rooted!(&in(cx) let algo = flush.borrow().clone());
456                if let Some(ref flush) = *algo {
457                    rooted!(&in(cx) let this_object = transform_obj.get());
458                    flush
459                        .Call_(cx, &this_object.handle(), self, ExceptionHandling::Rethrow)
460                        .unwrap_or_else(|e| {
461                            let p = Promise::new(cx, global);
462                            p.reject_error(cx, e);
463                            p
464                        })
465                } else {
466                    // Step 3. Let flushAlgorithm be an algorithm which returns a promise resolved with undefined.
467                    Promise::new_resolved(cx, global, ())
468                }
469            },
470            TransformerType::Decoder(decoder) => {
471                // <https://encoding.spec.whatwg.org/#dom-textdecoderstream>
472                // Step 8. Let flushAlgorithm be an algorithm which takes no
473                // arguments and runs the flush and enqueue algorithm with this.
474                flush_and_enqueue(cx, global, decoder, self)
475                    // <https://streams.spec.whatwg.org/#transformstream-set-up>
476                    // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps:
477                    // Step 6.1 Let result be the result of running flushAlgorithm,
478                    //      if flushAlgorithm was given, or null otherwise.
479                    // Step 6.2 If result is a Promise, then return result.
480                    // Note: Not applicable. The spec does NOT require flush_and_enqueue algo to return a Promise
481                    // Step 6.3 Return a promise resolved with undefined.
482                    .map(|_| Promise::new_resolved(cx, global, ()))
483                    .unwrap_or_else(|e| {
484                        // <https://streams.spec.whatwg.org/#transformstream-set-up>
485                        // Step 6.1 If this throws an exception e,
486                        let mut realm = enter_auto_realm(cx, self);
487                        let realm = &mut realm.current_realm();
488                        let p = Promise::new_in_realm(realm);
489                        // return a promise rejected with e.
490                        p.reject_error(realm, e);
491                        p
492                    })
493            },
494            TransformerType::Encoder(encoder) => {
495                // <https://encoding.spec.whatwg.org/#textencoderstream-encoder>
496                // Step 3. Let flushAlgorithm be an algorithm which runs the encode and flush algorithm with this.
497                encode_and_flush(cx, global, encoder, self)
498                    // <https://streams.spec.whatwg.org/#transformstream-set-up>
499                    // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps:
500                    // Step 6.1 Let result be the result of running flushAlgorithm,
501                    //      if flushAlgorithm was given, or null otherwise.
502                    // Step 6.2 If result is a Promise, then return result.
503                    // Note: Not applicable. The spec does NOT require encode_and_flush algo to return a Promise
504                    // Step 6.3 Return a promise resolved with undefined.
505                    .map(|_| Promise::new_resolved(cx, global, ()))
506                    .unwrap_or_else(|e| {
507                        // <https://streams.spec.whatwg.org/#transformstream-set-up>
508                        // Step 6.1 If this throws an exception e,
509                        let mut realm = enter_auto_realm(cx, self);
510                        let realm = &mut realm.current_realm();
511                        let p = Promise::new_in_realm(realm);
512                        // return a promise rejected with e.
513                        p.reject_error(realm, e);
514                        p
515                    })
516            },
517            TransformerType::Compressor(cs) => {
518                // <https://compression.spec.whatwg.org/#dom-compressionstream-compressionstream>
519                // Step 4. Let flushAlgorithm be an algorithm which takes no argument and runs the
520                // compress flush and enqueue algorithm with this.
521                compress_flush_and_enqueue(cx, global, cs, self)
522                    // <https://streams.spec.whatwg.org/#transformstream-set-up>
523                    // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps:
524                    // Step 6.1 Let result be the result of running flushAlgorithm,
525                    //      if flushAlgorithm was given, or null otherwise.
526                    // Step 6.2 If result is a Promise, then return result.
527                    // Note: Not applicable. The spec does NOT require compress_flush_and_enqueue
528                    // algo to return a Promise.
529                    // Step 6.3 Return a promise resolved with undefined.
530                    .map(|_| Promise::new_resolved(cx, global, ()))
531                    .unwrap_or_else(|e| {
532                        // <https://streams.spec.whatwg.org/#transformstream-set-up>
533                        // Step 6.1 If this throws an exception e,
534                        let mut realm = enter_auto_realm(cx, self);
535                        let realm = &mut realm.current_realm();
536                        let p = Promise::new_in_realm(realm);
537                        // return a promise rejected with e.
538                        p.reject_error(realm, e);
539                        p
540                    })
541            },
542            TransformerType::Decompressor(ds) => {
543                // <https://compression.spec.whatwg.org/#dom-decompressionstream-decompressionstream>
544                // Step 4. Let flushAlgorithm be an algorithm which takes no argument and runs the
545                // decompress flush and enqueue algorithm with this.
546                decompress_flush_and_enqueue(cx, global, ds, self)
547                    // <https://streams.spec.whatwg.org/#transformstream-set-up>
548                    // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps:
549                    // Step 6.1 Let result be the result of running flushAlgorithm,
550                    //      if flushAlgorithm was given, or null otherwise.
551                    // Step 6.2 If result is a Promise, then return result.
552                    // Note: Not applicable. The spec does NOT require decompress_flush_and_enqueue
553                    // algo to return a Promise.
554                    // Step 6.3 Return a promise resolved with undefined.
555                    .map(|_| Promise::new_resolved(cx, global, ()))
556                    .unwrap_or_else(|e| {
557                        // <https://streams.spec.whatwg.org/#transformstream-set-up>
558                        // Step 6.1 If this throws an exception e,
559                        let mut realm = enter_auto_realm(cx, self);
560                        let realm = &mut realm.current_realm();
561                        let p = Promise::new_in_realm(realm);
562                        // return a promise rejected with e.
563                        p.reject_error(realm, e);
564                        p
565                    })
566            },
567        };
568
569        Ok(result)
570    }
571
572    /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-enqueue>
573    #[expect(unsafe_code)]
574    pub(crate) fn enqueue(
575        &self,
576        cx: &mut JSContext,
577        global: &GlobalScope,
578        chunk: SafeHandleValue,
579    ) -> Fallible<()> {
580        // Let stream be controller.[[stream]].
581        let stream = self.stream.get().expect("stream is null");
582
583        // Let readableController be stream.[[readable]].[[controller]].
584        let readable = stream.get_readable();
585        let readable_controller = readable.get_default_controller();
586
587        // If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(readableController)
588        // is false, throw a TypeError exception.
589        if !readable_controller.can_close_or_enqueue() {
590            return Err(Error::Type(
591                c"ReadableStreamDefaultControllerCanCloseOrEnqueue is false".to_owned(),
592            ));
593        }
594
595        // Let enqueueResult be ReadableStreamDefaultControllerEnqueue(readableController, chunk).
596        // If enqueueResult is an abrupt completion,
597        if let Err(error) = readable_controller.enqueue(cx, chunk) {
598            // Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, enqueueResult.[[Value]]).
599            rooted!(&in(cx) let mut rooted_error = UndefinedValue());
600            error
601                .clone()
602                .to_jsval(cx, global, rooted_error.handle_mut());
603            stream.error_writable_and_unblock_write(cx, global, rooted_error.handle());
604
605            // Throw stream.[[readable]].[[storedError]].
606            unsafe {
607                if !JS_IsExceptionPending(cx.raw_cx()) {
608                    rooted!(&in(cx) let mut stored_error = UndefinedValue());
609                    readable.get_stored_error(stored_error.handle_mut());
610
611                    JS_SetPendingException(
612                        cx.raw_cx(),
613                        stored_error.handle().into(),
614                        ExceptionStackBehavior::Capture,
615                    );
616                }
617            }
618            return Err(error);
619        }
620
621        // Let backpressure be ! ReadableStreamDefaultControllerHasBackpressure(readableController).
622        let backpressure = readable_controller.has_backpressure();
623
624        // If backpressure is not stream.[[backpressure]],
625        if backpressure != stream.get_backpressure() {
626            // Assert: backpressure is true.
627            assert!(backpressure);
628
629            // Perform ! TransformStreamSetBackpressure(stream, true).
630            stream.set_backpressure(cx, global, true);
631        }
632        Ok(())
633    }
634
635    /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-error>
636    pub(crate) fn error(&self, cx: &mut JSContext, global: &GlobalScope, reason: SafeHandleValue) {
637        // Perform ! TransformStreamError(controller.[[stream]], e).
638        self.stream
639            .get()
640            .expect("stream is undefined")
641            .error(cx, global, reason);
642    }
643
644    /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-clear-algorithms>
645    pub(crate) fn clear_algorithms(&self) {
646        if let TransformerType::Js {
647            cancel,
648            flush,
649            transform,
650            ..
651        } = &self.transformer_type
652        {
653            // Set controller.[[transformAlgorithm]] to undefined.
654            transform.replace(None);
655
656            // Set controller.[[flushAlgorithm]] to undefined.
657            flush.replace(None);
658
659            // Set controller.[[cancelAlgorithm]] to undefined.
660            cancel.replace(None);
661        }
662    }
663
664    /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-terminate>
665    fn terminate(&self, cx: &mut JSContext, global: &GlobalScope) {
666        // Let stream be controller.[[stream]].
667        let stream = self.stream.get().expect("stream is null");
668
669        // Let readableController be stream.[[readable]].[[controller]].
670        let readable = stream.get_readable();
671        let readable_controller = readable.get_default_controller();
672
673        // Perform ! ReadableStreamDefaultControllerClose(readableController).
674        readable_controller.close(cx);
675
676        // Let error be a TypeError exception indicating that the stream has been terminated.
677        let error = Error::Type(c"stream has been terminated".to_owned());
678
679        // Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, error).
680        rooted!(&in(cx) let mut rooted_error = UndefinedValue());
681        error.to_jsval(cx, global, rooted_error.handle_mut());
682        stream.error_writable_and_unblock_write(cx, global, rooted_error.handle());
683    }
684}
685
686impl TransformStreamDefaultControllerMethods<crate::DomTypeHolder>
687    for TransformStreamDefaultController
688{
689    /// <https://streams.spec.whatwg.org/#ts-default-controller-desired-size>
690    fn GetDesiredSize(&self) -> Option<f64> {
691        // Let readableController be this.[[stream]].[[readable]].[[controller]].
692        let readable_controller = self
693            .stream
694            .get()
695            .expect("stream is null")
696            .get_readable()
697            .get_default_controller();
698
699        // Return ! ReadableStreamDefaultControllerGetDesiredSize(readableController).
700        readable_controller.get_desired_size()
701    }
702
703    /// <https://streams.spec.whatwg.org/#ts-default-controller-enqueue>
704    fn Enqueue(&self, cx: &mut JSContext, chunk: SafeHandleValue) -> Fallible<()> {
705        // Perform ? TransformStreamDefaultControllerEnqueue(this, chunk).
706        self.enqueue(cx, &self.global(), chunk)
707    }
708
709    /// <https://streams.spec.whatwg.org/#ts-default-controller-error>
710    fn Error(&self, cx: &mut JSContext, reason: SafeHandleValue) -> Fallible<()> {
711        // Perform ? TransformStreamDefaultControllerError(this, e).
712        self.error(cx, &self.global(), reason);
713        Ok(())
714    }
715
716    /// <https://streams.spec.whatwg.org/#ts-default-controller-terminate>
717    fn Terminate(&self, cx: &mut JSContext) -> Fallible<()> {
718        // Perform ? TransformStreamDefaultControllerTerminate(this).
719        self.terminate(cx, &self.global());
720        Ok(())
721    }
722}