/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::borrow::ToOwned; use std::cell::Cell; use dom_struct::dom_struct; use encoding_rs::Encoding; use js::rust::HandleObject; use crate::dom::bindings::codegen::Bindings::TextDecoderBinding; use crate::dom::bindings::codegen::Bindings::TextDecoderBinding::{ TextDecodeOptions, TextDecoderMethods, }; use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::{DOMString, USVString}; use crate::dom::globalscope::GlobalScope; use crate::dom::textdecodercommon::TextDecoderCommon; use crate::script_runtime::CanGc; /// #[dom_struct] #[allow(non_snake_case)] pub(crate) struct TextDecoder { reflector_: Reflector, /// decoder: TextDecoderCommon, /// do_not_flush: Cell, } #[allow(non_snake_case)] impl TextDecoder { fn new_inherited(encoding: &'static Encoding, fatal: bool, ignoreBOM: bool) -> TextDecoder { let decoder = TextDecoderCommon::new_inherited(encoding, fatal, ignoreBOM); TextDecoder { reflector_: Reflector::new(), decoder, do_not_flush: Cell::new(false), } } fn make_range_error() -> Fallible> { Err(Error::Range( "The given encoding is not supported.".to_owned(), )) } fn new( global: &GlobalScope, proto: Option, encoding: &'static Encoding, fatal: bool, ignoreBOM: bool, can_gc: CanGc, ) -> DomRoot { reflect_dom_object_with_proto( Box::new(TextDecoder::new_inherited(encoding, fatal, ignoreBOM)), global, proto, can_gc, ) } } #[allow(non_snake_case)] impl TextDecoderMethods for TextDecoder { /// fn Constructor( global: &GlobalScope, proto: Option, can_gc: CanGc, label: DOMString, options: &TextDecoderBinding::TextDecoderOptions, ) -> Fallible> { let encoding = match Encoding::for_label_no_replacement(label.as_bytes()) { None => return TextDecoder::make_range_error(), Some(enc) => enc, }; Ok(TextDecoder::new( global, proto, encoding, options.fatal, options.ignoreBOM, can_gc, )) } /// fn Encoding(&self) -> DOMString { DOMString::from(self.decoder.encoding().name().to_ascii_lowercase()) } /// fn Fatal(&self) -> bool { self.decoder.fatal() } /// fn IgnoreBOM(&self) -> bool { self.decoder.ignore_bom() } /// fn Decode( &self, input: Option, options: &TextDecodeOptions, ) -> Fallible { // Step 1. If this’s do not flush is false, then set this’s decoder to a new // instance of this’s encoding’s decoder, this’s I/O queue to the I/O queue // of bytes « end-of-queue », and this’s BOM seen to false. if !self.do_not_flush.get() { if self.decoder.ignore_bom() { self.decoder .decoder() .replace(self.decoder.encoding().new_decoder_without_bom_handling()); } else { self.decoder .decoder() .replace(self.decoder.encoding().new_decoder_with_bom_removal()); } self.decoder.io_queue().replace(Vec::new()); } // Step 2. Set this’s do not flush to options["stream"]. self.do_not_flush.set(options.stream); // Step 3. If input is given, then push a copy of input to this’s I/O queue. // Step 4. Let output be the I/O queue of scalar values « end-of-queue ». // Step 5. While true: // Step 5.1 Let item be the result of reading from this’s I/O queue. // Step 5.2 If item is end-of-queue and this’s do not flush is true, // then return the result of running serialize I/O queue with this and output. // Step 5.3 Otherwise: // Step 5.3.1 Let result be the result of processing an item with item, this’s decoder, // this’s I/O queue, output, and this’s error mode. // Step 5.3.2 If result is finished, then return the result of running serialize I/O // queue with this and output. self.decoder .decode(input.as_ref(), !options.stream) .map(USVString) } }