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