| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ |
| |
| use std::cell::RefCell; |
| use std::rc::Rc; |
| |
| use dom_struct::dom_struct; |
| use js::jsapi::{ |
| ExceptionStackBehavior, Heap, JS_IsExceptionPending, JS_SetPendingException, JSObject, |
| }; |
| use js::jsval::UndefinedValue; |
| use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue}; |
| |
| use super::bindings::cell::DomRefCell; |
| use super::bindings::codegen::Bindings::TransformerBinding::{ |
| TransformerCancelCallback, TransformerFlushCallback, TransformerTransformCallback, |
| }; |
| use super::types::TransformStream; |
| use crate::dom::bindings::callback::ExceptionHandling; |
| use crate::dom::bindings::codegen::Bindings::TransformStreamDefaultControllerBinding::TransformStreamDefaultControllerMethods; |
| use crate::dom::bindings::codegen::Bindings::TransformerBinding::Transformer; |
| use crate::dom::bindings::error::{Error, ErrorToJsval, Fallible}; |
| use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; |
| use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; |
| use crate::dom::globalscope::GlobalScope; |
| use crate::dom::promise::Promise; |
| use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler}; |
| use crate::dom::textdecodercommon::TextDecoderCommon; |
| use crate::dom::textdecoderstream::{decode_and_enqueue_a_chunk, flush_and_enqueue}; |
| use crate::dom::textencoderstream::{Encoder, encode_and_enqueue_a_chunk, encode_and_flush}; |
| use crate::realms::{InRealm, enter_realm}; |
| use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; |
| |
| impl js::gc::Rootable for TransformTransformPromiseRejection {} |
| |
| /// Reacting to transformPromise as part of |
| /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-perform-transform> |
| #[derive(JSTraceable, MallocSizeOf)] |
| #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] |
| struct TransformTransformPromiseRejection { |
| controller: Dom<TransformStreamDefaultController>, |
| } |
| |
| impl Callback for TransformTransformPromiseRejection { |
| /// Reacting to transformPromise with the following fulfillment steps: |
| #[allow(unsafe_code)] |
| fn callback(&self, cx: SafeJSContext, v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) { |
| // Perform ! TransformStreamError(controller.[[stream]], r). |
| self.controller |
| .error(cx, &self.controller.global(), v, can_gc); |
| |
| // Throw r. |
| // Note: this is done part of perform_transform(). |
| } |
| } |
| |
| /// The type of transformer algorithms we are using |
| #[derive(JSTraceable)] |
| pub(crate) enum TransformerType { |
| /// Algorithms provided by Js callbacks |
| Js { |
| /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-cancelalgorithm> |
| cancel: RefCell<Option<Rc<TransformerCancelCallback>>>, |
| |
| /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-flushalgorithm> |
| flush: RefCell<Option<Rc<TransformerFlushCallback>>>, |
| |
| /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-transformalgorithm> |
| transform: RefCell<Option<Rc<TransformerTransformCallback>>>, |
| |
| /// The JS object used as `this` when invoking sink algorithms. |
| transform_obj: Heap<*mut JSObject>, |
| }, |
| /// Algorithms supporting `TextDecoderStream` are implemented in Rust |
| /// |
| /// <https://encoding.spec.whatwg.org/#textdecodercommon> |
| Decoder(Rc<TextDecoderCommon>), |
| /// Algorithms supporting `TextEncoderStream` are implemented in Rust |
| /// |
| /// <https://encoding.spec.whatwg.org/#textencoderstream-encoder> |
| Encoder(Encoder), |
| } |
| |
| impl TransformerType { |
| pub(crate) fn new_from_js_transformer(transformer: &Transformer) -> TransformerType { |
| TransformerType::Js { |
| cancel: RefCell::new(transformer.cancel.clone()), |
| flush: RefCell::new(transformer.flush.clone()), |
| transform: RefCell::new(transformer.transform.clone()), |
| transform_obj: Default::default(), |
| } |
| } |
| } |
| |
| /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller> |
| #[dom_struct] |
| pub struct TransformStreamDefaultController { |
| reflector_: Reflector, |
| |
| /// The type of the underlying transformer used. Besides the JS variant, |
| /// there will be other variant(s) for `TextDecoderStream` |
| #[ignore_malloc_size_of = "transformer_type"] |
| transformer_type: TransformerType, |
| |
| /// <https://streams.spec.whatwg.org/#TransformStreamDefaultController-stream> |
| stream: MutNullableDom<TransformStream>, |
| |
| /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-finishpromise> |
| #[ignore_malloc_size_of = "Rc is hard"] |
| finish_promise: DomRefCell<Option<Rc<Promise>>>, |
| } |
| |
| impl TransformStreamDefaultController { |
| #[cfg_attr(crown, allow(crown::unrooted_must_root))] |
| fn new_inherited(transformer_type: TransformerType) -> TransformStreamDefaultController { |
| TransformStreamDefaultController { |
| reflector_: Reflector::new(), |
| transformer_type, |
| stream: MutNullableDom::new(None), |
| finish_promise: DomRefCell::new(None), |
| } |
| } |
| |
| #[cfg_attr(crown, allow(crown::unrooted_must_root))] |
| pub(crate) fn new( |
| global: &GlobalScope, |
| transformer_type: TransformerType, |
| can_gc: CanGc, |
| ) -> DomRoot<TransformStreamDefaultController> { |
| reflect_dom_object( |
| Box::new(TransformStreamDefaultController::new_inherited( |
| transformer_type, |
| )), |
| global, |
| can_gc, |
| ) |
| } |
| |
| /// Setting the JS object after the heap has settled down. |
| /// |
| /// Note that this has no effect if the transformer type is not `TransformerType::Js` |
| pub(crate) fn set_transform_obj(&self, this_object: SafeHandleObject) { |
| if let TransformerType::Js { transform_obj, .. } = &self.transformer_type { |
| transform_obj.set(*this_object) |
| } else { |
| unreachable!("Non-Js transformer type should not set transform_obj") |
| } |
| } |
| |
| pub(crate) fn set_stream(&self, stream: &TransformStream) { |
| self.stream.set(Some(stream)); |
| } |
| |
| pub(crate) fn get_finish_promise(&self) -> Option<Rc<Promise>> { |
| self.finish_promise.borrow().clone() |
| } |
| |
| pub(crate) fn set_finish_promise(&self, promise: Rc<Promise>) { |
| *self.finish_promise.borrow_mut() = Some(promise); |
| } |
| |
| /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-perform-transform> |
| pub(crate) fn transform_stream_default_controller_perform_transform( |
| &self, |
| cx: SafeJSContext, |
| global: &GlobalScope, |
| chunk: SafeHandleValue, |
| can_gc: CanGc, |
| ) -> Fallible<Rc<Promise>> { |
| // Let transformPromise be the result of performing controller.[[transformAlgorithm]], passing chunk. |
| let transform_promise = self.perform_transform(cx, global, chunk, can_gc)?; |
| |
| // Return the result of reacting to transformPromise with the following rejection steps given the argument r: |
| rooted!(in(*cx) let mut reject_handler = Some(TransformTransformPromiseRejection { |
| controller: Dom::from_ref(self), |
| })); |
| |
| let handler = PromiseNativeHandler::new( |
| global, |
| None, |
| reject_handler.take().map(|h| Box::new(h) as Box<_>), |
| can_gc, |
| ); |
| let realm = enter_realm(global); |
| let comp = InRealm::Entered(&realm); |
| transform_promise.append_native_handler(&handler, comp, can_gc); |
| |
| Ok(transform_promise) |
| } |
| |
| pub(crate) fn perform_transform( |
| &self, |
| cx: SafeJSContext, |
| global: &GlobalScope, |
| chunk: SafeHandleValue, |
| can_gc: CanGc, |
| ) -> Fallible<Rc<Promise>> { |
| let result = match &self.transformer_type { |
| // <https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer> |
| TransformerType::Js { |
| transform, |
| transform_obj, |
| .. |
| } => { |
| // Step 5. If transformerDict["transform"] exists, set |
| // transformAlgorithm to an algorithm which takes an argument |
| // chunk and returns the result of invoking |
| // transformerDict["transform"] with argument list « chunk, |
| // controller » and callback this value transformer. |
| let algo = transform.borrow().clone(); |
| if let Some(transform) = algo { |
| rooted!(in(*cx) let this_object = transform_obj.get()); |
| transform |
| .Call_( |
| &this_object.handle(), |
| chunk, |
| self, |
| ExceptionHandling::Rethrow, |
| can_gc, |
| ) |
| .unwrap_or_else(|e| { |
| let p = Promise::new(global, can_gc); |
| p.reject_error(e, can_gc); |
| p |
| }) |
| } else { |
| // Step 2. Let transformAlgorithm be the following steps, taking a chunk argument: |
| // Let result be TransformStreamDefaultControllerEnqueue(controller, chunk). |
| // If result is an abrupt completion, return a promise rejected with result.[[Value]]. |
| if let Err(error) = self.enqueue(cx, global, chunk, can_gc) { |
| rooted!(in(*cx) let mut error_val = UndefinedValue()); |
| error.to_jsval(cx, global, error_val.handle_mut(), can_gc); |
| Promise::new_rejected(global, cx, error_val.handle(), can_gc) |
| } else { |
| // Otherwise, return a promise resolved with undefined. |
| Promise::new_resolved(global, cx, (), can_gc) |
| } |
| } |
| }, |
| TransformerType::Decoder(decoder) => { |
| // <https://encoding.spec.whatwg.org/#dom-textdecoderstream> |
| // Step 7. Let transformAlgorithm be an algorithm which takes a |
| // chunk argument and runs the decode and enqueue a chunk |
| // algorithm with this and chunk. |
| decode_and_enqueue_a_chunk(cx, global, chunk, decoder, self, can_gc) |
| // <https://streams.spec.whatwg.org/#transformstream-set-up> |
| // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps given a value chunk: |
| // Step 5.1 Let result be the result of running transformAlgorithm given chunk. |
| // Step 5.2 If result is a Promise, then return result. |
| // Note: not applicable, the spec does NOT require deode_and_enqueue_a_chunk() to return a Promise |
| // Step 5.3 Return a promise resolved with undefined. |
| .map(|_| Promise::new_resolved(global, cx, (), can_gc)) |
| .unwrap_or_else(|e| { |
| // <https://streams.spec.whatwg.org/#transformstream-set-up> |
| // Step 5.1 If this throws an exception e, |
| let realm = enter_realm(self); |
| let p = Promise::new_in_current_realm((&realm).into(), can_gc); |
| // return a promise rejected with e. |
| p.reject_error(e, can_gc); |
| p |
| }) |
| }, |
| TransformerType::Encoder(encoder) => { |
| // <https://encoding.spec.whatwg.org/#dom-textencoderstream> |
| // Step 2. Let transformAlgorithm be an algorithm which takes a chunk argument and runs the encode |
| // and enqueue a chunk algorithm with this and chunk. |
| encode_and_enqueue_a_chunk(cx, global, chunk, encoder, self, can_gc) |
| // <https://streams.spec.whatwg.org/#transformstream-set-up> |
| // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps given a value chunk: |
| // Step 5.1 Let result be the result of running transformAlgorithm given chunk. |
| // Step 5.2 If result is a Promise, then return result. |
| // Note: not applicable, the spec does NOT require encode_and_enqueue_a_chunk() to return a Promise |
| // Step 5.3 Return a promise resolved with undefined. |
| .map(|_| Promise::new_resolved(global, cx, (), can_gc)) |
| .unwrap_or_else(|e| { |
| // <https://streams.spec.whatwg.org/#transformstream-set-up> |
| // Step 5.1 If this throws an exception e, |
| let realm = enter_realm(self); |
| let p = Promise::new_in_current_realm((&realm).into(), can_gc); |
| // return a promise rejected with e. |
| p.reject_error(e, can_gc); |
| p |
| }) |
| }, |
| }; |
| |
| Ok(result) |
| } |
| |
| pub(crate) fn perform_cancel( |
| &self, |
| cx: SafeJSContext, |
| global: &GlobalScope, |
| chunk: SafeHandleValue, |
| can_gc: CanGc, |
| ) -> Fallible<Rc<Promise>> { |
| let result = match &self.transformer_type { |
| // <https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer> |
| TransformerType::Js { |
| cancel, |
| transform_obj, |
| .. |
| } => { |
| // Step 7. If transformerDict["cancel"] exists, set |
| // cancelAlgorithm to an algorithm which takes an argument |
| // reason and returns the result of invoking |
| // transformerDict["cancel"] with argument list « reason » and |
| // callback this value transformer. |
| let algo = cancel.borrow().clone(); |
| if let Some(cancel) = algo { |
| rooted!(in(*cx) let this_object = transform_obj.get()); |
| cancel |
| .Call_( |
| &this_object.handle(), |
| chunk, |
| ExceptionHandling::Rethrow, |
| can_gc, |
| ) |
| .unwrap_or_else(|e| { |
| let p = Promise::new(global, can_gc); |
| p.reject_error(e, can_gc); |
| p |
| }) |
| } else { |
| // Step 4. Let cancelAlgorithm be an algorithm which returns a promise resolved with undefined. |
| Promise::new_resolved(global, cx, (), can_gc) |
| } |
| }, |
| TransformerType::Decoder(_) => { |
| // <https://streams.spec.whatwg.org/#transformstream-set-up> |
| // Step 7. Let cancelAlgorithmWrapper be an algorithm that runs these steps given a value reason: |
| // Step 7.1 Let result be the result of running cancelAlgorithm given reason, |
| // if cancelAlgorithm was given, or null otherwise |
| // Note: `TextDecoderStream` does NOT specify a cancel algorithm. |
| // Step 7.2 If result is a Promise, then return result. |
| // Note: Not applicable. |
| // Step 7.3 Return a promise resolved with undefined. |
| Promise::new_resolved(global, cx, (), can_gc) |
| }, |
| TransformerType::Encoder(_) => { |
| // <https://streams.spec.whatwg.org/#transformstream-set-up> |
| // Step 7. Let cancelAlgorithmWrapper be an algorithm that runs these steps given a value reason: |
| // Step 7.1 Let result be the result of running cancelAlgorithm given reason, |
| // if cancelAlgorithm was given, or null otherwise |
| // Note: `TextDecoderStream` does NOT specify a cancel algorithm. |
| // Step 7.2 If result is a Promise, then return result. |
| // Note: Not applicable. |
| // Step 7.3 Return a promise resolved with undefined. |
| Promise::new_resolved(global, cx, (), can_gc) |
| }, |
| }; |
| |
| Ok(result) |
| } |
| |
| pub(crate) fn perform_flush( |
| &self, |
| cx: SafeJSContext, |
| global: &GlobalScope, |
| can_gc: CanGc, |
| ) -> Fallible<Rc<Promise>> { |
| let result = match &self.transformer_type { |
| // <https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer> |
| TransformerType::Js { |
| flush, |
| transform_obj, |
| .. |
| } => { |
| // Step 6. If transformerDict["flush"] exists, set flushAlgorithm to an |
| // algorithm which returns the result of invoking |
| // transformerDict["flush"] with argument list « controller » |
| // and callback this value transformer. |
| let algo = flush.borrow().clone(); |
| if let Some(flush) = algo { |
| rooted!(in(*cx) let this_object = transform_obj.get()); |
| flush |
| .Call_( |
| &this_object.handle(), |
| self, |
| ExceptionHandling::Rethrow, |
| can_gc, |
| ) |
| .unwrap_or_else(|e| { |
| let p = Promise::new(global, can_gc); |
| p.reject_error(e, can_gc); |
| p |
| }) |
| } else { |
| // Step 3. Let flushAlgorithm be an algorithm which returns a promise resolved with undefined. |
| Promise::new_resolved(global, cx, (), can_gc) |
| } |
| }, |
| TransformerType::Decoder(decoder) => { |
| // <https://encoding.spec.whatwg.org/#dom-textdecoderstream> |
| // Step 8. Let flushAlgorithm be an algorithm which takes no |
| // arguments and runs the flush and enqueue algorithm with this. |
| flush_and_enqueue(cx, global, decoder, self, can_gc) |
| // <https://streams.spec.whatwg.org/#transformstream-set-up> |
| // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps: |
| // Step 6.1 Let result be the result of running flushAlgorithm, |
| // if flushAlgorithm was given, or null otherwise. |
| // Step 6.2 If result is a Promise, then return result. |
| // Note: Not applicable. The spec does NOT require flush_and_enqueue algo to return a Promise |
| // Step 6.3 Return a promise resolved with undefined. |
| .map(|_| Promise::new_resolved(global, cx, (), can_gc)) |
| .unwrap_or_else(|e| { |
| // <https://streams.spec.whatwg.org/#transformstream-set-up> |
| // Step 6.1 If this throws an exception e, |
| let realm = enter_realm(self); |
| let p = Promise::new_in_current_realm((&realm).into(), can_gc); |
| // return a promise rejected with e. |
| p.reject_error(e, can_gc); |
| p |
| }) |
| }, |
| TransformerType::Encoder(encoder) => { |
| // <https://encoding.spec.whatwg.org/#textencoderstream-encoder> |
| // Step 3. Let flushAlgorithm be an algorithm which runs the encode and flush algorithm with this. |
| encode_and_flush(cx, global, encoder, self, can_gc) |
| // <https://streams.spec.whatwg.org/#transformstream-set-up> |
| // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps: |
| // Step 6.1 Let result be the result of running flushAlgorithm, |
| // if flushAlgorithm was given, or null otherwise. |
| // Step 6.2 If result is a Promise, then return result. |
| // Note: Not applicable. The spec does NOT require encode_and_flush algo to return a Promise |
| // Step 6.3 Return a promise resolved with undefined. |
| .map(|_| Promise::new_resolved(global, cx, (), can_gc)) |
| .unwrap_or_else(|e| { |
| // <https://streams.spec.whatwg.org/#transformstream-set-up> |
| // Step 6.1 If this throws an exception e, |
| let realm = enter_realm(self); |
| let p = Promise::new_in_current_realm((&realm).into(), can_gc); |
| // return a promise rejected with e. |
| p.reject_error(e, can_gc); |
| p |
| }) |
| }, |
| }; |
| |
| Ok(result) |
| } |
| |
| /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-enqueue> |
| #[allow(unsafe_code)] |
| pub(crate) fn enqueue( |
| &self, |
| cx: SafeJSContext, |
| global: &GlobalScope, |
| chunk: SafeHandleValue, |
| can_gc: CanGc, |
| ) -> Fallible<()> { |
| // Let stream be controller.[[stream]]. |
| let stream = self.stream.get().expect("stream is null"); |
| |
| // Let readableController be stream.[[readable]].[[controller]]. |
| let readable = stream.get_readable(); |
| let readable_controller = readable.get_default_controller(); |
| |
| // If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(readableController) |
| // is false, throw a TypeError exception. |
| if !readable_controller.can_close_or_enqueue() { |
| return Err(Error::Type( |
| "ReadableStreamDefaultControllerCanCloseOrEnqueue is false".to_owned(), |
| )); |
| } |
| |
| // Let enqueueResult be ReadableStreamDefaultControllerEnqueue(readableController, chunk). |
| // If enqueueResult is an abrupt completion, |
| if let Err(error) = readable_controller.enqueue(cx, chunk, can_gc) { |
| // Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, enqueueResult.[[Value]]). |
| rooted!(in(*cx) let mut rooted_error = UndefinedValue()); |
| error |
| .clone() |
| .to_jsval(cx, global, rooted_error.handle_mut(), can_gc); |
| stream.error_writable_and_unblock_write(cx, global, rooted_error.handle(), can_gc); |
| |
| // Throw stream.[[readable]].[[storedError]]. |
| unsafe { |
| if !JS_IsExceptionPending(*cx) { |
| rooted!(in(*cx) let mut stored_error = UndefinedValue()); |
| readable.get_stored_error(stored_error.handle_mut()); |
| |
| JS_SetPendingException( |
| *cx, |
| stored_error.handle().into(), |
| ExceptionStackBehavior::Capture, |
| ); |
| } |
| } |
| return Err(error); |
| } |
| |
| // Let backpressure be ! ReadableStreamDefaultControllerHasBackpressure(readableController). |
| let backpressure = readable_controller.has_backpressure(); |
| |
| // If backpressure is not stream.[[backpressure]], |
| if backpressure != stream.get_backpressure() { |
| // Assert: backpressure is true. |
| assert!(backpressure); |
| |
| // Perform ! TransformStreamSetBackpressure(stream, true). |
| stream.set_backpressure(global, true, can_gc); |
| } |
| Ok(()) |
| } |
| |
| /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-error> |
| pub(crate) fn error( |
| &self, |
| cx: SafeJSContext, |
| global: &GlobalScope, |
| reason: SafeHandleValue, |
| can_gc: CanGc, |
| ) { |
| // Perform ! TransformStreamError(controller.[[stream]], e). |
| self.stream |
| .get() |
| .expect("stream is undefined") |
| .error(cx, global, reason, can_gc); |
| } |
| |
| /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-clear-algorithms> |
| pub(crate) fn clear_algorithms(&self) { |
| if let TransformerType::Js { |
| cancel, |
| flush, |
| transform, |
| .. |
| } = &self.transformer_type |
| { |
| // Set controller.[[transformAlgorithm]] to undefined. |
| transform.replace(None); |
| |
| // Set controller.[[flushAlgorithm]] to undefined. |
| flush.replace(None); |
| |
| // Set controller.[[cancelAlgorithm]] to undefined. |
| cancel.replace(None); |
| } |
| } |
| |
| /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-terminate> |
| pub(crate) fn terminate(&self, cx: SafeJSContext, global: &GlobalScope, can_gc: CanGc) { |
| // Let stream be controller.[[stream]]. |
| let stream = self.stream.get().expect("stream is null"); |
| |
| // Let readableController be stream.[[readable]].[[controller]]. |
| let readable = stream.get_readable(); |
| let readable_controller = readable.get_default_controller(); |
| |
| // Perform ! ReadableStreamDefaultControllerClose(readableController). |
| readable_controller.close(can_gc); |
| |
| // Let error be a TypeError exception indicating that the stream has been terminated. |
| let error = Error::Type("stream has been terminated".to_owned()); |
| |
| // Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, error). |
| rooted!(in(*cx) let mut rooted_error = UndefinedValue()); |
| error.to_jsval(cx, global, rooted_error.handle_mut(), can_gc); |
| stream.error_writable_and_unblock_write(cx, global, rooted_error.handle(), can_gc); |
| } |
| } |
| |
| #[allow(non_snake_case)] |
| impl TransformStreamDefaultControllerMethods<crate::DomTypeHolder> |
| for TransformStreamDefaultController |
| { |
| /// <https://streams.spec.whatwg.org/#ts-default-controller-desired-size> |
| fn GetDesiredSize(&self) -> Option<f64> { |
| // Let readableController be this.[[stream]].[[readable]].[[controller]]. |
| let readable_controller = self |
| .stream |
| .get() |
| .expect("stream is null") |
| .get_readable() |
| .get_default_controller(); |
| |
| // Return ! ReadableStreamDefaultControllerGetDesiredSize(readableController). |
| readable_controller.get_desired_size() |
| } |
| |
| /// <https://streams.spec.whatwg.org/#ts-default-controller-enqueue> |
| fn Enqueue(&self, cx: SafeJSContext, chunk: SafeHandleValue, can_gc: CanGc) -> Fallible<()> { |
| // Perform ? TransformStreamDefaultControllerEnqueue(this, chunk). |
| self.enqueue(cx, &self.global(), chunk, can_gc) |
| } |
| |
| /// <https://streams.spec.whatwg.org/#ts-default-controller-error> |
| fn Error(&self, cx: SafeJSContext, reason: SafeHandleValue, can_gc: CanGc) -> Fallible<()> { |
| // Perform ? TransformStreamDefaultControllerError(this, e). |
| self.error(cx, &self.global(), reason, can_gc); |
| Ok(()) |
| } |
| |
| /// <https://streams.spec.whatwg.org/#ts-default-controller-terminate> |
| fn Terminate(&self, can_gc: CanGc) -> Fallible<()> { |
| // Perform ? TransformStreamDefaultControllerTerminate(this). |
| self.terminate(GlobalScope::get_cx(), &self.global(), can_gc); |
| Ok(()) |
| } |
| } |