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