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::encoding::textdecodercommon::TextDecoderCommon;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct TextDecoder {
reflector_: Reflector,
decoder: TextDecoderCommon,
do_not_flush: Cell<bool>,
}
#[expect(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<DomRoot<TextDecoder>> {
Err(Error::Range(
c"The given encoding is not supported.".to_owned(),
))
}
fn new(
global: &GlobalScope,
proto: Option<HandleObject>,
encoding: &'static Encoding,
fatal: bool,
ignoreBOM: bool,
can_gc: CanGc,
) -> DomRoot<TextDecoder> {
reflect_dom_object_with_proto(
Box::new(TextDecoder::new_inherited(encoding, fatal, ignoreBOM)),
global,
proto,
can_gc,
)
}
}
impl TextDecoderMethods<crate::DomTypeHolder> for TextDecoder {
fn Constructor(
global: &GlobalScope,
proto: Option<HandleObject>,
can_gc: CanGc,
label: DOMString,
options: &TextDecoderBinding::TextDecoderOptions,
) -> Fallible<DomRoot<TextDecoder>> {
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<ArrayBufferViewOrArrayBuffer>,
options: &TextDecodeOptions,
) -> Fallible<USVString> {
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());
}
self.do_not_flush.set(options.stream);
self.decoder
.decode(input.as_ref(), !options.stream)
.map(USVString)
}
}