Skip to main content

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