/* 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::cell::RefCell; use encoding_rs::{Decoder, DecoderResult, Encoding}; use crate::dom::bindings::codegen::UnionTypes::ArrayBufferViewOrArrayBuffer; use crate::dom::bindings::error::{Error, Fallible}; /// The shared part of `TextDecoder` and `TextDecoderStream` /// /// Note that other than the three attributes defined in the `TextDecoderCommon` /// interface in the WebIDL, this also performs decoding. /// /// #[allow(non_snake_case)] #[derive(JSTraceable, MallocSizeOf)] pub(crate) struct TextDecoderCommon { /// #[no_trace] encoding: &'static Encoding, /// fatal: bool, /// ignoreBOM: bool, /// The native decoder that is used to perform decoding /// /// #[ignore_malloc_size_of = "defined in encoding_rs"] #[no_trace] decoder: RefCell, /// io_queue: RefCell>, } #[allow(non_snake_case)] impl TextDecoderCommon { pub(crate) fn new_inherited( encoding: &'static Encoding, fatal: bool, ignoreBOM: bool, ) -> TextDecoderCommon { let decoder = if ignoreBOM { encoding.new_decoder_without_bom_handling() } else { encoding.new_decoder_with_bom_removal() }; TextDecoderCommon { encoding, fatal, ignoreBOM, decoder: RefCell::new(decoder), io_queue: RefCell::new(Vec::new()), } } /// pub(crate) fn encoding(&self) -> &'static Encoding { self.encoding } /// pub(crate) fn decoder(&self) -> &RefCell { &self.decoder } /// pub(crate) fn io_queue(&self) -> &RefCell> { &self.io_queue } /// pub(crate) fn fatal(&self) -> bool { self.fatal } /// pub(crate) fn ignore_bom(&self) -> bool { self.ignoreBOM } /// Shared by `TextDecoder` and `TextDecoderStream` /// /// /// #[allow(unsafe_code)] pub(crate) fn decode( &self, input: Option<&ArrayBufferViewOrArrayBuffer>, last: bool, ) -> Fallible { // // Step 3. If input is given, then push a copy of input to this’s I/O queue. // // // Step 2. Push a copy of bufferSource to decoder’s I/O queue. // // NOTE: try to avoid this copy unless there are bytes left let mut io_queue = self.io_queue.borrow_mut(); let input = match &input { Some(ArrayBufferViewOrArrayBuffer::ArrayBufferView(a)) => unsafe { if io_queue.is_empty() { a.as_slice() } else { io_queue.extend_from_slice(a.as_slice()); &io_queue[..] } }, Some(ArrayBufferViewOrArrayBuffer::ArrayBuffer(a)) => unsafe { if io_queue.is_empty() { a.as_slice() } else { io_queue.extend_from_slice(a.as_slice()); &io_queue[..] } }, None => &io_queue[..], }; let mut decoder = self.decoder.borrow_mut(); let (output, read) = if self.fatal { // // Step 4. Let output be the I/O queue of scalar values « end-of-queue ». // // // Step 3. Let output be the I/O queue of scalar values « end-of-queue ». let mut output = String::with_capacity( decoder .max_utf8_buffer_length_without_replacement(input.len()) .ok_or_else(|| { Error::Type("Expected UTF8 buffer length would overflow".to_owned()) })?, ); // Note: The two algorithms below are implemented in // `encoding_rs::Decoder::decode_to_string_without_replacement` // // // 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 4. While true: // Step 4.1 Let item be the result of reading from decoder’s I/O queue. // Step 4.2 If item is end-of-queue: // Step 4.2.1 Let outputChunk be the result of running serialize I/O queue with decoder and output. // Step 4.2.2 If outputChunk is not the empty string, then enqueue outputChunk in decoder’s transform. // Step 4.2.3 Return. // Step 4.3 Let result be the result of processing an item with item, decoder’s decoder, // decoder’s I/O queue, output, and decoder’s error mode. // Step 4.4 If result is error, then throw a TypeError. let (result, read) = decoder.decode_to_string_without_replacement(input, &mut output, last); match result { // // Step 5.3.2 If result is finished, then return the result of running serialize I/O // queue with this and output. DecoderResult::InputEmpty => (output, read), // // Step 5.3.3 Otherwise, if result is error, throw a TypeError. DecoderResult::Malformed(_, _) => { return Err(Error::Type("Decoding failed".to_owned())); }, DecoderResult::OutputFull => { unreachable!("output is allocated with sufficient capacity") }, } } else { // // Step 4. Let output be the I/O queue of scalar values « end-of-queue ». let mut output = String::with_capacity(decoder.max_utf8_buffer_length(input.len()).ok_or_else( || Error::Type("Expected UTF8 buffer length would overflow".to_owned()), )?); // Note: The two algorithms below are implemented in // `encoding_rs::Decoder::decode_to_string` // // // 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 4. While true: // Step 4.1 Let item be the result of reading from decoder’s I/O queue. // Step 4.2 If item is end-of-queue: // Step 4.2.1 Let outputChunk be the result of running serialize I/O queue with decoder and output. // Step 4.2.2 If outputChunk is not the empty string, then enqueue outputChunk in decoder’s transform. // Step 4.2.3 Return. // Step 4.3 Let result be the result of processing an item with item, decoder’s decoder, // decoder’s I/O queue, output, and decoder’s error mode. // Step 4.4 If result is error, then throw a TypeError. let (result, read, _replaced) = decoder.decode_to_string(input, &mut output, last); match result { // // Step 5.3.2 If result is finished, then return the result of running serialize I/O // queue with this and output. encoding_rs::CoderResult::InputEmpty => (output, read), encoding_rs::CoderResult::OutputFull => { unreachable!("output is allocated with sufficient capacity") }, } }; let (_consumed, remaining) = input.split_at(read); *io_queue = remaining.to_vec(); Ok(output) } }