script/dom/
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};
12
13use crate::DomTypes;
14use crate::dom::bindings::codegen::Bindings::TextDecoderBinding;
15use crate::dom::bindings::codegen::Bindings::TextDecoderStreamBinding::TextDecoderStreamMethods;
16use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer;
17use crate::dom::bindings::error::{Error, Fallible};
18use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
19use crate::dom::bindings::root::{Dom, DomRoot};
20use crate::dom::bindings::str::DOMString;
21use crate::dom::globalscope::GlobalScope;
22use crate::dom::textdecodercommon::TextDecoderCommon;
23use crate::dom::transformstreamdefaultcontroller::TransformerType;
24use crate::dom::types::{TransformStream, TransformStreamDefaultController};
25use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
26
27#[allow(unsafe_code)]
29pub(crate) fn decode_and_enqueue_a_chunk(
30 cx: SafeJSContext,
31 global: &GlobalScope,
32 chunk: SafeHandleValue,
33 decoder: &TextDecoderCommon,
34 controller: &TransformStreamDefaultController,
35 can_gc: CanGc,
36) -> Fallible<()> {
37 let conversion_result = unsafe {
39 ArrayBufferViewOrArrayBuffer::from_jsval(*cx, chunk, ()).map_err(|_| {
40 Error::Type("Unable to convert chunk into ArrayBuffer or ArrayBufferView".to_string())
41 })?
42 };
43 let buffer_source = conversion_result.get_success_value().ok_or_else(|| {
44 Error::Type("Unable to convert chunk into ArrayBuffer or ArrayBufferView".to_string())
45 })?;
46
47 let output_chunk = decoder.decode(Some(buffer_source), false)?;
58
59 if output_chunk.is_empty() {
62 return Ok(());
63 }
64 rooted!(in(*cx) let mut rval = UndefinedValue());
65 unsafe { output_chunk.to_jsval(*cx, rval.handle_mut()) };
66 controller.enqueue(cx, global, rval.handle(), can_gc)
67}
68
69#[allow(unsafe_code)]
71pub(crate) fn flush_and_enqueue(
72 cx: SafeJSContext,
73 global: &GlobalScope,
74 decoder: &TextDecoderCommon,
75 controller: &TransformStreamDefaultController,
76 can_gc: CanGc,
77) -> Fallible<()> {
78 let output_chunk = decoder.decode(None, true)?;
89
90 if output_chunk.is_empty() {
93 return Ok(());
94 }
95 rooted!(in(*cx) let mut rval = UndefinedValue());
96 unsafe { output_chunk.to_jsval(*cx, rval.handle_mut()) };
97 controller.enqueue(cx, global, rval.handle(), can_gc)
98}
99
100#[dom_struct]
102pub(crate) struct TextDecoderStream {
103 reflector_: Reflector,
104
105 #[ignore_malloc_size_of = "Rc is hard"]
107 decoder: Rc<TextDecoderCommon>,
108
109 transform: Dom<TransformStream>,
111}
112
113#[allow(non_snake_case)]
114impl TextDecoderStream {
115 fn new_inherited(
116 decoder: Rc<TextDecoderCommon>,
117 transform: &TransformStream,
118 ) -> TextDecoderStream {
119 TextDecoderStream {
120 reflector_: Reflector::new(),
121 decoder,
122 transform: Dom::from_ref(transform),
123 }
124 }
125
126 fn new_with_proto(
127 cx: SafeJSContext,
128 global: &GlobalScope,
129 proto: Option<SafeHandleObject>,
130 encoding: &'static Encoding,
131 fatal: bool,
132 ignoreBOM: bool,
133 can_gc: CanGc,
134 ) -> Fallible<DomRoot<Self>> {
135 let decoder = Rc::new(TextDecoderCommon::new_inherited(encoding, fatal, ignoreBOM));
136 let transformer_type = TransformerType::Decoder(decoder.clone());
137
138 let transform_stream = TransformStream::new_with_proto(global, None, can_gc);
139 transform_stream.set_up(cx, global, transformer_type, can_gc)?;
140
141 Ok(reflect_dom_object_with_proto(
142 Box::new(TextDecoderStream::new_inherited(decoder, &transform_stream)),
143 global,
144 proto,
145 can_gc,
146 ))
147 }
148}
149
150#[allow(non_snake_case)]
151impl TextDecoderStreamMethods<crate::DomTypeHolder> for TextDecoderStream {
152 fn Constructor(
154 global: &GlobalScope,
155 proto: Option<SafeHandleObject>,
156 can_gc: CanGc,
157 label: DOMString,
158 options: &TextDecoderBinding::TextDecoderOptions,
159 ) -> Fallible<DomRoot<TextDecoderStream>> {
160 let encoding = match Encoding::for_label_no_replacement(label.as_bytes()) {
161 Some(enc) => enc,
162 None => {
163 return Err(Error::Range(
164 "The given encoding is not supported".to_owned(),
165 ));
166 },
167 };
168
169 Self::new_with_proto(
170 GlobalScope::get_cx(),
171 global,
172 proto,
173 encoding,
174 options.fatal,
175 options.ignoreBOM,
176 can_gc,
177 )
178 }
179
180 fn Encoding(&self) -> DOMString {
182 DOMString::from(self.decoder.encoding().name().to_ascii_lowercase())
183 }
184
185 fn Fatal(&self) -> bool {
187 self.decoder.fatal()
188 }
189
190 fn IgnoreBOM(&self) -> bool {
192 self.decoder.ignore_bom()
193 }
194
195 fn Readable(&self) -> DomRoot<<crate::DomTypeHolder as DomTypes>::ReadableStream> {
197 self.transform.get_readable()
198 }
199
200 fn Writable(&self) -> DomRoot<<crate::DomTypeHolder as DomTypes>::WritableStream> {
202 self.transform.get_writable()
203 }
204}