script/dom/
textdecoderstream.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::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/// <https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk>
28#[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    // Step 1. Let bufferSource be the result of converting chunk to an AllowSharedBufferSource.
38    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    // Step 2. Push a copy of bufferSource to decoder’s I/O queue.
48    // Step 3. Let output be the I/O queue of scalar values « end-of-queue ».
49    // Step 4. While true:
50    // Step 4.1 Let item be the result of reading from decoder’s I/O queue.
51    // Step 4.2 If item is end-of-queue:
52    // Step 4.2.1 Let outputChunk be the result of running serialize I/O queue with decoder and output.
53    // Step 4.2.3 Return.
54    // Step 4.3 Let result be the result of processing an item with item, decoder’s decoder,
55    //      decoder’s I/O queue, output, and decoder’s error mode.
56    // Step 4.4 If result is error, then throw a TypeError.
57    let output_chunk = decoder.decode(Some(buffer_source), false)?;
58
59    // Step 4.2.2 If outputChunk is not the empty string, then enqueue
60    //      outputChunk in decoder’s transform.
61    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/// <https://encoding.spec.whatwg.org/#flush-and-enqueue>
70#[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    // Step 1. Let output be the I/O queue of scalar values « end-of-queue ».
79    // Step 2. While true:
80    // Step 2.1 Let item be the result of reading from decoder’s I/O queue.
81    // Step 2.2 Let result be the result of processing an item with item,
82    //      decoder’s decoder, decoder’s I/O queue, output, and decoder’s error mode.
83    // Step 2.3 If result is finished:
84    // Step 2.3.1 Let outputChunk be the result of running serialize I/O queue
85    //      with decoder and output.
86    // Step 2.3.3 Return.
87    // Step 2.3.4 Otherwise, if result is error, throw a TypeError.
88    let output_chunk = decoder.decode(None, true)?;
89
90    // Step 2.3.2 If outputChunk is not the empty string, then enqueue
91    //      outputChunk in decoder’s transform.
92    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/// <https://encoding.spec.whatwg.org/#textdecoderstream>
101#[dom_struct]
102pub(crate) struct TextDecoderStream {
103    reflector_: Reflector,
104
105    /// <https://encoding.spec.whatwg.org/#textdecodercommon>
106    #[ignore_malloc_size_of = "Rc is hard"]
107    decoder: Rc<TextDecoderCommon>,
108
109    /// <https://streams.spec.whatwg.org/#generictransformstream>
110    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    /// <https://encoding.spec.whatwg.org/#dom-textdecoderstream>
153    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    /// <https://encoding.spec.whatwg.org/#dom-textdecoder-encoding>
181    fn Encoding(&self) -> DOMString {
182        DOMString::from(self.decoder.encoding().name().to_ascii_lowercase())
183    }
184
185    /// <https://encoding.spec.whatwg.org/#dom-textdecoder-fatal>
186    fn Fatal(&self) -> bool {
187        self.decoder.fatal()
188    }
189
190    /// <https://encoding.spec.whatwg.org/#dom-textdecoder-ignorebom>
191    fn IgnoreBOM(&self) -> bool {
192        self.decoder.ignore_bom()
193    }
194
195    /// <https://streams.spec.whatwg.org/#dom-generictransformstream-readable>
196    fn Readable(&self) -> DomRoot<<crate::DomTypeHolder as DomTypes>::ReadableStream> {
197        self.transform.get_readable()
198    }
199
200    /// <https://streams.spec.whatwg.org/#dom-generictransformstream-writable>
201    fn Writable(&self) -> DomRoot<<crate::DomTypeHolder as DomTypes>::WritableStream> {
202        self.transform.get_writable()
203    }
204}