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}