script/dom/
textdecoder.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::borrow::ToOwned;
6use std::cell::Cell;
7
8use dom_struct::dom_struct;
9use encoding_rs::Encoding;
10use js::rust::HandleObject;
11
12use crate::dom::bindings::codegen::Bindings::TextDecoderBinding;
13use crate::dom::bindings::codegen::Bindings::TextDecoderBinding::{
14    TextDecodeOptions, TextDecoderMethods,
15};
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::DomRoot;
20use crate::dom::bindings::str::{DOMString, USVString};
21use crate::dom::globalscope::GlobalScope;
22use crate::dom::textdecodercommon::TextDecoderCommon;
23use crate::script_runtime::CanGc;
24
25/// <https://encoding.spec.whatwg.org/#textdecoder>
26#[dom_struct]
27#[allow(non_snake_case)]
28pub(crate) struct TextDecoder {
29    reflector_: Reflector,
30
31    /// <https://encoding.spec.whatwg.org/#textdecodercommon>
32    decoder: TextDecoderCommon,
33
34    /// <https://encoding.spec.whatwg.org/#textdecoder-do-not-flush-flag>
35    do_not_flush: Cell<bool>,
36}
37
38#[allow(non_snake_case)]
39impl TextDecoder {
40    fn new_inherited(encoding: &'static Encoding, fatal: bool, ignoreBOM: bool) -> TextDecoder {
41        let decoder = TextDecoderCommon::new_inherited(encoding, fatal, ignoreBOM);
42        TextDecoder {
43            reflector_: Reflector::new(),
44            decoder,
45            do_not_flush: Cell::new(false),
46        }
47    }
48
49    fn make_range_error() -> Fallible<DomRoot<TextDecoder>> {
50        Err(Error::Range(
51            "The given encoding is not supported.".to_owned(),
52        ))
53    }
54
55    fn new(
56        global: &GlobalScope,
57        proto: Option<HandleObject>,
58        encoding: &'static Encoding,
59        fatal: bool,
60        ignoreBOM: bool,
61        can_gc: CanGc,
62    ) -> DomRoot<TextDecoder> {
63        reflect_dom_object_with_proto(
64            Box::new(TextDecoder::new_inherited(encoding, fatal, ignoreBOM)),
65            global,
66            proto,
67            can_gc,
68        )
69    }
70}
71
72#[allow(non_snake_case)]
73impl TextDecoderMethods<crate::DomTypeHolder> for TextDecoder {
74    /// <https://encoding.spec.whatwg.org/#dom-textdecoder>
75    fn Constructor(
76        global: &GlobalScope,
77        proto: Option<HandleObject>,
78        can_gc: CanGc,
79        label: DOMString,
80        options: &TextDecoderBinding::TextDecoderOptions,
81    ) -> Fallible<DomRoot<TextDecoder>> {
82        let encoding = match Encoding::for_label_no_replacement(label.as_bytes()) {
83            None => return TextDecoder::make_range_error(),
84            Some(enc) => enc,
85        };
86        Ok(TextDecoder::new(
87            global,
88            proto,
89            encoding,
90            options.fatal,
91            options.ignoreBOM,
92            can_gc,
93        ))
94    }
95
96    /// <https://encoding.spec.whatwg.org/#dom-textdecoder-encoding>
97    fn Encoding(&self) -> DOMString {
98        DOMString::from(self.decoder.encoding().name().to_ascii_lowercase())
99    }
100
101    /// <https://encoding.spec.whatwg.org/#dom-textdecoder-fatal>
102    fn Fatal(&self) -> bool {
103        self.decoder.fatal()
104    }
105
106    /// <https://encoding.spec.whatwg.org/#dom-textdecoder-ignorebom>
107    fn IgnoreBOM(&self) -> bool {
108        self.decoder.ignore_bom()
109    }
110
111    /// <https://encoding.spec.whatwg.org/#dom-textdecoder-decode>
112    fn Decode(
113        &self,
114        input: Option<ArrayBufferViewOrArrayBuffer>,
115        options: &TextDecodeOptions,
116    ) -> Fallible<USVString> {
117        // Step 1. If this’s do not flush is false, then set this’s decoder to a new
118        // instance of this’s encoding’s decoder, this’s I/O queue to the I/O queue
119        // of bytes « end-of-queue », and this’s BOM seen to false.
120        if !self.do_not_flush.get() {
121            if self.decoder.ignore_bom() {
122                self.decoder
123                    .decoder()
124                    .replace(self.decoder.encoding().new_decoder_without_bom_handling());
125            } else {
126                self.decoder
127                    .decoder()
128                    .replace(self.decoder.encoding().new_decoder_with_bom_removal());
129            }
130            self.decoder.io_queue().replace(Vec::new());
131        }
132
133        // Step 2. Set this’s do not flush to options["stream"].
134        self.do_not_flush.set(options.stream);
135
136        // Step 3. If input is given, then push a copy of input to this’s I/O queue.
137        // Step 4. Let output be the I/O queue of scalar values « end-of-queue ».
138        // Step 5. While true:
139        // Step 5.1 Let item be the result of reading from this’s I/O queue.
140        // Step 5.2 If item is end-of-queue and this’s do not flush is true,
141        //      then return the result of running serialize I/O queue with this and output.
142        // Step 5.3 Otherwise:
143        // Step 5.3.1 Let result be the result of processing an item with item, this’s decoder,
144        //      this’s I/O queue, output, and this’s error mode.
145        // Step 5.3.2 If result is finished, then return the result of running serialize I/O
146        //      queue with this and output.
147        self.decoder
148            .decode(input.as_ref(), !options.stream)
149            .map(USVString)
150    }
151}