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