script/dom/encoding/
textdecoderstream.rs1use std::rc::Rc;
6
7use dom_struct::dom_struct;
8use encoding_rs::Encoding;
9use js::conversions::{FromJSValConvertible, ToJSValConvertible};
10use js::jsval::UndefinedValue;
11use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue};
12use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
13
14use crate::DomTypes;
15use crate::dom::bindings::codegen::Bindings::TextDecoderBinding;
16use crate::dom::bindings::codegen::Bindings::TextDecoderStreamBinding::TextDecoderStreamMethods;
17use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
18use crate::dom::bindings::error::{Error, Fallible};
19use crate::dom::bindings::root::{Dom, DomRoot};
20use crate::dom::bindings::str::DOMString;
21use crate::dom::encoding::textdecodercommon::TextDecoderCommon;
22use crate::dom::globalscope::GlobalScope;
23use crate::dom::stream::transformstreamdefaultcontroller::TransformerType;
24use crate::dom::types::{TransformStream, TransformStreamDefaultController};
25
26pub(crate) fn decode_and_enqueue_a_chunk(
28 cx: &mut js::context::JSContext,
29 global: &GlobalScope,
30 chunk: SafeHandleValue,
31 decoder: &TextDecoderCommon,
32 controller: &TransformStreamDefaultController,
33) -> Fallible<()> {
34 let conversion_result =
36 ArrayBufferViewOrArrayBuffer::safe_from_jsval(cx, chunk, ()).map_err(|_| {
37 Error::Type(c"Unable to convert chunk into ArrayBuffer or ArrayBufferView".to_owned())
38 })?;
39 let buffer_source = conversion_result.get_success_value().ok_or_else(|| {
40 Error::Type(c"Unable to convert chunk into ArrayBuffer or ArrayBufferView".to_owned())
41 })?;
42
43 let output_chunk = decoder.decode(cx.no_gc(), Some(buffer_source), false)?;
54
55 if output_chunk.is_empty() {
58 return Ok(());
59 }
60 rooted!(&in(cx) let mut rval = UndefinedValue());
61 output_chunk.safe_to_jsval(cx, rval.handle_mut());
62 controller.enqueue(cx, global, rval.handle())
63}
64
65pub(crate) fn flush_and_enqueue(
67 cx: &mut js::context::JSContext,
68 global: &GlobalScope,
69 decoder: &TextDecoderCommon,
70 controller: &TransformStreamDefaultController,
71) -> Fallible<()> {
72 let output_chunk = decoder.decode(cx.no_gc(), None, true)?;
83
84 if output_chunk.is_empty() {
87 return Ok(());
88 }
89 rooted!(&in(cx) let mut rval = UndefinedValue());
90 output_chunk.safe_to_jsval(cx, rval.handle_mut());
91 controller.enqueue(cx, global, rval.handle())
92}
93
94#[dom_struct]
96pub(crate) struct TextDecoderStream {
97 reflector_: Reflector,
98
99 #[conditional_malloc_size_of]
101 decoder: Rc<TextDecoderCommon>,
102
103 transform: Dom<TransformStream>,
105}
106
107#[expect(non_snake_case)]
108impl TextDecoderStream {
109 fn new_inherited(
110 decoder: Rc<TextDecoderCommon>,
111 transform: &TransformStream,
112 ) -> TextDecoderStream {
113 TextDecoderStream {
114 reflector_: Reflector::new(),
115 decoder,
116 transform: Dom::from_ref(transform),
117 }
118 }
119
120 pub(crate) fn new_with_proto(
121 cx: &mut js::context::JSContext,
122 global: &GlobalScope,
123 proto: Option<SafeHandleObject>,
124 encoding: &'static Encoding,
125 fatal: bool,
126 ignoreBOM: bool,
127 ) -> Fallible<DomRoot<Self>> {
128 let decoder = Rc::new(TextDecoderCommon::new_inherited(encoding, fatal, ignoreBOM));
129 let transformer_type = TransformerType::Decoder(decoder.clone());
130
131 let transform_stream = TransformStream::new_with_proto(cx, global, None);
132 transform_stream.set_up(cx, global, transformer_type)?;
133
134 Ok(reflect_dom_object_with_proto(
135 cx,
136 Box::new(TextDecoderStream::new_inherited(decoder, &transform_stream)),
137 global,
138 proto,
139 ))
140 }
141}
142
143impl TextDecoderStreamMethods<crate::DomTypeHolder> for TextDecoderStream {
144 fn Constructor(
146 cx: &mut js::context::JSContext,
147 global: &GlobalScope,
148 proto: Option<SafeHandleObject>,
149 label: DOMString,
150 options: &TextDecoderBinding::TextDecoderOptions,
151 ) -> Fallible<DomRoot<TextDecoderStream>> {
152 let encoding = match Encoding::for_label_no_replacement(&label.as_bytes()) {
153 Some(enc) => enc,
154 None => {
155 return Err(Error::Range(
156 c"The given encoding is not supported".to_owned(),
157 ));
158 },
159 };
160
161 Self::new_with_proto(
162 cx,
163 global,
164 proto,
165 encoding,
166 options.fatal,
167 options.ignoreBOM,
168 )
169 }
170
171 fn Encoding(&self) -> DOMString {
173 DOMString::from(self.decoder.encoding().name().to_ascii_lowercase())
174 }
175
176 fn Fatal(&self) -> bool {
178 self.decoder.fatal()
179 }
180
181 fn IgnoreBOM(&self) -> bool {
183 self.decoder.ignore_bom()
184 }
185
186 fn Readable(&self) -> DomRoot<<crate::DomTypeHolder as DomTypes>::ReadableStream> {
188 self.transform.get_readable()
189 }
190
191 fn Writable(&self) -> DomRoot<<crate::DomTypeHolder as DomTypes>::WritableStream> {
193 self.transform.get_writable()
194 }
195}