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_and_cx};
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};
25use crate::script_runtime::CanGc;
26
27#[expect(unsafe_code)]
29pub(crate) fn decode_and_enqueue_a_chunk(
30 cx: &mut js::context::JSContext,
31 global: &GlobalScope,
32 chunk: SafeHandleValue,
33 decoder: &TextDecoderCommon,
34 controller: &TransformStreamDefaultController,
35) -> Fallible<()> {
36 let conversion_result =
38 ArrayBufferViewOrArrayBuffer::safe_from_jsval(cx, chunk, ()).map_err(|_| {
39 Error::Type(c"Unable to convert chunk into ArrayBuffer or ArrayBufferView".to_owned())
40 })?;
41 let buffer_source = conversion_result.get_success_value().ok_or_else(|| {
42 Error::Type(c"Unable to convert chunk into ArrayBuffer or ArrayBufferView".to_owned())
43 })?;
44
45 let output_chunk = decoder.decode(Some(buffer_source), false)?;
56
57 if output_chunk.is_empty() {
60 return Ok(());
61 }
62 rooted!(&in(cx) let mut rval = UndefinedValue());
63 unsafe { output_chunk.to_jsval(cx.raw_cx(), rval.handle_mut()) };
64 controller.enqueue(cx, global, rval.handle())
65}
66
67#[expect(unsafe_code)]
69pub(crate) fn flush_and_enqueue(
70 cx: &mut js::context::JSContext,
71 global: &GlobalScope,
72 decoder: &TextDecoderCommon,
73 controller: &TransformStreamDefaultController,
74) -> Fallible<()> {
75 let output_chunk = decoder.decode(None, true)?;
86
87 if output_chunk.is_empty() {
90 return Ok(());
91 }
92 rooted!(&in(cx) let mut rval = UndefinedValue());
93 unsafe { output_chunk.to_jsval(cx.raw_cx(), rval.handle_mut()) };
94 controller.enqueue(cx, global, rval.handle())
95}
96
97#[dom_struct]
99pub(crate) struct TextDecoderStream {
100 reflector_: Reflector,
101
102 #[conditional_malloc_size_of]
104 decoder: Rc<TextDecoderCommon>,
105
106 transform: Dom<TransformStream>,
108}
109
110#[expect(non_snake_case)]
111impl TextDecoderStream {
112 fn new_inherited(
113 decoder: Rc<TextDecoderCommon>,
114 transform: &TransformStream,
115 ) -> TextDecoderStream {
116 TextDecoderStream {
117 reflector_: Reflector::new(),
118 decoder,
119 transform: Dom::from_ref(transform),
120 }
121 }
122
123 fn new_with_proto(
124 cx: &mut js::context::JSContext,
125 global: &GlobalScope,
126 proto: Option<SafeHandleObject>,
127 encoding: &'static Encoding,
128 fatal: bool,
129 ignoreBOM: bool,
130 ) -> Fallible<DomRoot<Self>> {
131 let decoder = Rc::new(TextDecoderCommon::new_inherited(encoding, fatal, ignoreBOM));
132 let transformer_type = TransformerType::Decoder(decoder.clone());
133
134 let transform_stream = TransformStream::new_with_proto(global, None, CanGc::from_cx(cx));
135 transform_stream.set_up(cx, global, transformer_type)?;
136
137 Ok(reflect_dom_object_with_proto_and_cx(
138 Box::new(TextDecoderStream::new_inherited(decoder, &transform_stream)),
139 global,
140 proto,
141 cx,
142 ))
143 }
144}
145
146impl TextDecoderStreamMethods<crate::DomTypeHolder> for TextDecoderStream {
147 fn Constructor(
149 cx: &mut js::context::JSContext,
150 global: &GlobalScope,
151 proto: Option<SafeHandleObject>,
152 label: DOMString,
153 options: &TextDecoderBinding::TextDecoderOptions,
154 ) -> Fallible<DomRoot<TextDecoderStream>> {
155 let encoding = match Encoding::for_label_no_replacement(&label.as_bytes()) {
156 Some(enc) => enc,
157 None => {
158 return Err(Error::Range(
159 c"The given encoding is not supported".to_owned(),
160 ));
161 },
162 };
163
164 Self::new_with_proto(
165 cx,
166 global,
167 proto,
168 encoding,
169 options.fatal,
170 options.ignoreBOM,
171 )
172 }
173
174 fn Encoding(&self) -> DOMString {
176 DOMString::from(self.decoder.encoding().name().to_ascii_lowercase())
177 }
178
179 fn Fatal(&self) -> bool {
181 self.decoder.fatal()
182 }
183
184 fn IgnoreBOM(&self) -> bool {
186 self.decoder.ignore_bom()
187 }
188
189 fn Readable(&self) -> DomRoot<<crate::DomTypeHolder as DomTypes>::ReadableStream> {
191 self.transform.get_readable()
192 }
193
194 fn Writable(&self) -> DomRoot<<crate::DomTypeHolder as DomTypes>::WritableStream> {
196 self.transform.get_writable()
197 }
198}