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