diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 80918eda497..20f26e19655 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -590,6 +590,8 @@ pub(crate) mod testworkletglobalscope; pub(crate) mod text; pub(crate) mod textcontrol; pub(crate) mod textdecoder; +pub(crate) mod textdecodercommon; +pub(crate) mod textdecoderstream; pub(crate) mod textencoder; pub(crate) mod textmetrics; pub(crate) mod texttrack; diff --git a/components/script/dom/textdecoder.rs b/components/script/dom/textdecoder.rs index 1396f6589f0..b25a756116d 100644 --- a/components/script/dom/textdecoder.rs +++ b/components/script/dom/textdecoder.rs @@ -3,10 +3,10 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::borrow::ToOwned; -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use dom_struct::dom_struct; -use encoding_rs::{Decoder, DecoderResult, Encoding}; +use encoding_rs::Encoding; use js::rust::HandleObject; use crate::dom::bindings::codegen::Bindings::TextDecoderBinding; @@ -19,37 +19,29 @@ 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, - #[no_trace] - encoding: &'static Encoding, - fatal: bool, - ignoreBOM: bool, - #[ignore_malloc_size_of = "defined in encoding_rs"] - #[no_trace] - decoder: RefCell, - in_stream: RefCell>, + + /// + 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(), - encoding, - fatal, - ignoreBOM, - decoder: RefCell::new(if ignoreBOM { - encoding.new_decoder() - } else { - encoding.new_decoder_without_bom_handling() - }), - in_stream: RefCell::new(Vec::new()), + decoder, do_not_flush: Cell::new(false), } } @@ -77,6 +69,7 @@ impl TextDecoder { } } +#[allow(non_snake_case)] impl TextDecoderMethods for TextDecoder { /// fn Constructor( @@ -100,85 +93,59 @@ impl TextDecoderMethods for TextDecoder { )) } - // https://encoding.spec.whatwg.org/#dom-textdecoder-encoding + /// fn Encoding(&self) -> DOMString { - DOMString::from(self.encoding.name().to_ascii_lowercase()) + DOMString::from(self.decoder.encoding().name().to_ascii_lowercase()) } - // https://encoding.spec.whatwg.org/#dom-textdecoder-fatal + /// fn Fatal(&self) -> bool { - self.fatal + self.decoder.fatal() } - // https://encoding.spec.whatwg.org/#dom-textdecoder-ignorebom + /// fn IgnoreBOM(&self) -> bool { - self.ignoreBOM + self.decoder.ignore_bom() } - // https://encoding.spec.whatwg.org/#dom-textdecoder-decode + /// fn Decode( &self, input: Option, options: &TextDecodeOptions, ) -> Fallible { - // Step 1. + // 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.ignoreBOM { + if self.decoder.ignore_bom() { self.decoder - .replace(self.encoding.new_decoder_without_bom_handling()); + .decoder() + .replace(self.decoder.encoding().new_decoder_without_bom_handling()); } else { - self.decoder.replace(self.encoding.new_decoder()); + self.decoder + .decoder() + .replace(self.decoder.encoding().new_decoder_with_bom_removal()); } - self.in_stream.replace(Vec::new()); + self.decoder.io_queue().replace(Vec::new()); } - // Step 2. + // Step 2. Set this’s do not flush to options["stream"]. self.do_not_flush.set(options.stream); - // Step 3. - match input { - Some(ArrayBufferViewOrArrayBuffer::ArrayBufferView(ref a)) => { - self.in_stream.borrow_mut().extend_from_slice(&a.to_vec()); - }, - Some(ArrayBufferViewOrArrayBuffer::ArrayBuffer(ref a)) => { - self.in_stream.borrow_mut().extend_from_slice(&a.to_vec()); - }, - None => {}, - }; - - let mut decoder = self.decoder.borrow_mut(); - let (remaining, s) = { - let mut in_stream = self.in_stream.borrow_mut(); - - let (remaining, s) = if self.fatal { - // Step 4. - let mut out_stream = String::with_capacity( - decoder - .max_utf8_buffer_length_without_replacement(in_stream.len()) - .unwrap(), - ); - // Step 5: Implemented by encoding_rs::Decoder. - match decoder.decode_to_string_without_replacement( - &in_stream, - &mut out_stream, - !options.stream, - ) { - (DecoderResult::InputEmpty, read) => (in_stream.split_off(read), out_stream), - // Step 5.3.3. - _ => return Err(Error::Type("Decoding failed".to_owned())), - } - } else { - // Step 4. - let mut out_stream = - String::with_capacity(decoder.max_utf8_buffer_length(in_stream.len()).unwrap()); - // Step 5: Implemented by encoding_rs::Decoder. - let (_result, read, _replaced) = - decoder.decode_to_string(&in_stream, &mut out_stream, !options.stream); - (in_stream.split_off(read), out_stream) - }; - (remaining, s) - }; - self.in_stream.replace(remaining); - Ok(USVString(s)) + // 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) } } diff --git a/components/script/dom/textdecodercommon.rs b/components/script/dom/textdecodercommon.rs new file mode 100644 index 00000000000..4840537740f --- /dev/null +++ b/components/script/dom/textdecodercommon.rs @@ -0,0 +1,227 @@ +/* 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) + } +} diff --git a/components/script/dom/textdecoderstream.rs b/components/script/dom/textdecoderstream.rs new file mode 100644 index 00000000000..5f5412a6ca8 --- /dev/null +++ b/components/script/dom/textdecoderstream.rs @@ -0,0 +1,204 @@ +/* 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::rc::Rc; + +use dom_struct::dom_struct; +use encoding_rs::Encoding; +use js::conversions::{FromJSValConvertible, ToJSValConvertible}; +use js::jsval::UndefinedValue; +use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue}; + +use crate::DomTypes; +use crate::dom::bindings::codegen::Bindings::TextDecoderBinding; +use crate::dom::bindings::codegen::Bindings::TextDecoderStreamBinding::TextDecoderStreamMethods; +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::{Dom, DomRoot}; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use crate::dom::textdecodercommon::TextDecoderCommon; +use crate::dom::transformstreamdefaultcontroller::TransformerType; +use crate::dom::types::{TransformStream, TransformStreamDefaultController}; +use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; + +/// +#[allow(unsafe_code)] +pub(crate) fn decode_and_enqueue_a_chunk( + cx: SafeJSContext, + global: &GlobalScope, + chunk: SafeHandleValue, + decoder: &TextDecoderCommon, + controller: &TransformStreamDefaultController, + can_gc: CanGc, +) -> Fallible<()> { + // Step 1. Let bufferSource be the result of converting chunk to an AllowSharedBufferSource. + let conversion_result = unsafe { + ArrayBufferViewOrArrayBuffer::from_jsval(*cx, chunk, ()).map_err(|_| { + Error::Type("Unable to convert chunk into ArrayBuffer or ArrayBufferView".to_string()) + })? + }; + let buffer_source = conversion_result.get_success_value().ok_or_else(|| { + Error::Type("Unable to convert chunk into ArrayBuffer or ArrayBufferView".to_string()) + })?; + + // Step 2. Push a copy of bufferSource to decoder’s I/O queue. + // Step 3. Let output be the I/O queue of scalar values « end-of-queue ». + // 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.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 output_chunk = decoder.decode(Some(buffer_source), false)?; + + // Step 4.2.2 If outputChunk is not the empty string, then enqueue + // outputChunk in decoder’s transform. + if output_chunk.is_empty() { + return Ok(()); + } + rooted!(in(*cx) let mut rval = UndefinedValue()); + unsafe { output_chunk.to_jsval(*cx, rval.handle_mut()) }; + controller.enqueue(cx, global, rval.handle(), can_gc) +} + +/// +#[allow(unsafe_code)] +pub(crate) fn flush_and_enqueue( + cx: SafeJSContext, + global: &GlobalScope, + decoder: &TextDecoderCommon, + controller: &TransformStreamDefaultController, + can_gc: CanGc, +) -> Fallible<()> { + // Step 1. Let output be the I/O queue of scalar values « end-of-queue ». + // Step 2. While true: + // Step 2.1 Let item be the result of reading from decoder’s I/O queue. + // Step 2.2 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 2.3 If result is finished: + // Step 2.3.1 Let outputChunk be the result of running serialize I/O queue + // with decoder and output. + // Step 2.3.3 Return. + // Step 2.3.4 Otherwise, if result is error, throw a TypeError. + let output_chunk = decoder.decode(None, true)?; + + // Step 2.3.2 If outputChunk is not the empty string, then enqueue + // outputChunk in decoder’s transform. + if output_chunk.is_empty() { + return Ok(()); + } + rooted!(in(*cx) let mut rval = UndefinedValue()); + unsafe { output_chunk.to_jsval(*cx, rval.handle_mut()) }; + controller.enqueue(cx, global, rval.handle(), can_gc) +} + +/// +#[dom_struct] +pub(crate) struct TextDecoderStream { + reflector_: Reflector, + + /// + #[ignore_malloc_size_of = "Rc is hard"] + decoder: Rc, + + /// + transform: Dom, +} + +#[allow(non_snake_case)] +impl TextDecoderStream { + fn new_inherited( + decoder: Rc, + transform: &TransformStream, + ) -> TextDecoderStream { + TextDecoderStream { + reflector_: Reflector::new(), + decoder, + transform: Dom::from_ref(transform), + } + } + + fn new_with_proto( + cx: SafeJSContext, + global: &GlobalScope, + proto: Option, + encoding: &'static Encoding, + fatal: bool, + ignoreBOM: bool, + can_gc: CanGc, + ) -> Fallible> { + let decoder = Rc::new(TextDecoderCommon::new_inherited(encoding, fatal, ignoreBOM)); + let transformer_type = TransformerType::Decoder(decoder.clone()); + + let transform_stream = TransformStream::new_with_proto(global, None, can_gc); + transform_stream.set_up(cx, global, transformer_type, can_gc)?; + + Ok(reflect_dom_object_with_proto( + Box::new(TextDecoderStream::new_inherited(decoder, &transform_stream)), + global, + proto, + can_gc, + )) + } +} + +#[allow(non_snake_case)] +impl TextDecoderStreamMethods for TextDecoderStream { + /// + 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()) { + Some(enc) => enc, + None => { + return Err(Error::Range( + "The given encoding is not supported".to_owned(), + )); + }, + }; + + Self::new_with_proto( + GlobalScope::get_cx(), + 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 Readable(&self) -> DomRoot<::ReadableStream> { + self.transform.get_readable() + } + + /// + fn Writable(&self) -> DomRoot<::WritableStream> { + self.transform.get_writable() + } +} diff --git a/components/script/dom/transformstream.rs b/components/script/dom/transformstream.rs index 6ed96b3034a..4ee0af02cd0 100644 --- a/components/script/dom/transformstream.rs +++ b/components/script/dom/transformstream.rs @@ -36,6 +36,7 @@ use crate::dom::countqueuingstrategy::{extract_high_water_mark, extract_size_alg use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; use crate::dom::readablestream::{ReadableStream, create_readable_stream}; +use crate::dom::transformstreamdefaultcontroller::TransformerType; use crate::dom::types::PromiseNativeHandler; use crate::dom::underlyingsourcecontainer::UnderlyingSourceType; use crate::dom::writablestream::create_writable_stream; @@ -436,6 +437,60 @@ impl TransformStream { ) } + /// Creates and set up the newly created transform stream following + /// + pub(crate) fn set_up( + &self, + cx: SafeJSContext, + global: &GlobalScope, + transformer_type: TransformerType, + can_gc: CanGc, + ) -> Fallible<()> { + // Step1. Let writableHighWaterMark be 1. + let writable_high_water_mark = 1.0; + + // Step 2. Let writableSizeAlgorithm be an algorithm that returns 1. + let writable_size_algorithm = extract_size_algorithm(&Default::default(), can_gc); + + // Step 3. Let readableHighWaterMark be 0. + let readable_high_water_mark = 0.0; + + // Step 4. Let readableSizeAlgorithm be an algorithm that returns 1. + let readable_size_algorithm = extract_size_algorithm(&Default::default(), can_gc); + + // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps given a value chunk: + // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps: + // Step 7. Let cancelAlgorithmWrapper be an algorithm that runs these steps given a value reason: + // NOTE: These steps are implemented in `TransformStreamDefaultController::new` + + // Step 8. Let startPromise be a promise resolved with undefined. + let start_promise = Promise::new_resolved(global, cx, (), can_gc); + + // Step 9. Perform ! InitializeTransformStream(stream, startPromise, + // writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark, + // readableSizeAlgorithm). + self.initialize( + cx, + global, + start_promise.clone(), + writable_high_water_mark, + writable_size_algorithm, + readable_high_water_mark, + readable_size_algorithm, + can_gc, + )?; + + // Step 10. Let controller be a new TransformStreamDefaultController. + let controller = TransformStreamDefaultController::new(global, transformer_type, can_gc); + + // Step 11. Perform ! SetUpTransformStreamDefaultController(stream, + // controller, transformAlgorithmWrapper, flushAlgorithmWrapper, + // cancelAlgorithmWrapper). + self.set_up_transform_stream_default_controller(&controller); + + Ok(()) + } + pub(crate) fn get_controller(&self) -> DomRoot { self.controller.get().expect("controller is not set") } @@ -568,7 +623,8 @@ impl TransformStream { can_gc: CanGc, ) { // Let controller be a new TransformStreamDefaultController. - let controller = TransformStreamDefaultController::new(global, transformer, can_gc); + let transformer_type = TransformerType::new_from_js_transformer(transformer); + let controller = TransformStreamDefaultController::new(global, transformer_type, can_gc); // Let transformAlgorithm be the following steps, taking a chunk argument: // Let result be TransformStreamDefaultControllerEnqueue(controller, chunk). @@ -892,6 +948,7 @@ impl TransformStream { } } +#[allow(non_snake_case)] impl TransformStreamMethods for TransformStream { /// #[allow(unsafe_code)] diff --git a/components/script/dom/transformstreamdefaultcontroller.rs b/components/script/dom/transformstreamdefaultcontroller.rs index 41813ef6f96..54fb7a693a5 100644 --- a/components/script/dom/transformstreamdefaultcontroller.rs +++ b/components/script/dom/transformstreamdefaultcontroller.rs @@ -14,17 +14,20 @@ use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue} use super::bindings::cell::DomRefCell; use super::bindings::codegen::Bindings::TransformerBinding::{ - Transformer, TransformerCancelCallback, TransformerFlushCallback, TransformerTransformCallback, + TransformerCancelCallback, TransformerFlushCallback, TransformerTransformCallback, }; use super::types::TransformStream; use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::codegen::Bindings::TransformStreamDefaultControllerBinding::TransformStreamDefaultControllerMethods; +use crate::dom::bindings::codegen::Bindings::TransformerBinding::Transformer; use crate::dom::bindings::error::{Error, ErrorToJsval, Fallible}; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler}; +use crate::dom::textdecodercommon::TextDecoderCommon; +use crate::dom::textdecoderstream::{decode_and_enqueue_a_chunk, flush_and_enqueue}; use crate::realms::{InRealm, enter_realm}; use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; @@ -51,26 +54,49 @@ impl Callback for TransformTransformPromiseRejection { } } +/// The type of transformer algorithms we are using +#[derive(JSTraceable)] +pub(crate) enum TransformerType { + /// Algorithms provided by Js callbacks + Js { + /// + cancel: RefCell>>, + + /// + flush: RefCell>>, + + /// + transform: RefCell>>, + + /// The JS object used as `this` when invoking sink algorithms. + transform_obj: Heap<*mut JSObject>, + }, + /// Algorithms supporting `TextDecoderStream` are implemented in Rust + /// + /// + Decoder(Rc), +} + +impl TransformerType { + pub(crate) fn new_from_js_transformer(transformer: &Transformer) -> TransformerType { + TransformerType::Js { + cancel: RefCell::new(transformer.cancel.clone()), + flush: RefCell::new(transformer.flush.clone()), + transform: RefCell::new(transformer.transform.clone()), + transform_obj: Default::default(), + } + } +} + /// #[dom_struct] pub struct TransformStreamDefaultController { reflector_: Reflector, - /// - #[ignore_malloc_size_of = "Rc is hard"] - cancel: RefCell>>, - - /// - #[ignore_malloc_size_of = "Rc is hard"] - flush: RefCell>>, - - /// - #[ignore_malloc_size_of = "Rc is hard"] - transform: RefCell>>, - - /// The JS object used as `this` when invoking sink algorithms. - #[ignore_malloc_size_of = "mozjs"] - transform_obj: Heap<*mut JSObject>, + /// The type of the underlying transformer used. Besides the JS variant, + /// there will be other variant(s) for `TextDecoderStream` + #[ignore_malloc_size_of = "transformer_type"] + transformer_type: TransformerType, /// stream: MutNullableDom, @@ -82,34 +108,39 @@ pub struct TransformStreamDefaultController { impl TransformStreamDefaultController { #[cfg_attr(crown, allow(crown::unrooted_must_root))] - fn new_inherited(transformer: &Transformer) -> TransformStreamDefaultController { + fn new_inherited(transformer_type: TransformerType) -> TransformStreamDefaultController { TransformStreamDefaultController { reflector_: Reflector::new(), - cancel: RefCell::new(transformer.cancel.clone()), - flush: RefCell::new(transformer.flush.clone()), - transform: RefCell::new(transformer.transform.clone()), - finish_promise: DomRefCell::new(None), + transformer_type, stream: MutNullableDom::new(None), - transform_obj: Default::default(), + finish_promise: DomRefCell::new(None), } } #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn new( global: &GlobalScope, - transformer: &Transformer, + transformer_type: TransformerType, can_gc: CanGc, ) -> DomRoot { reflect_dom_object( - Box::new(TransformStreamDefaultController::new_inherited(transformer)), + Box::new(TransformStreamDefaultController::new_inherited( + transformer_type, + )), global, can_gc, ) } /// Setting the JS object after the heap has settled down. + /// + /// Note that this has no effect if the transformer type is not `TransformerType::Js` pub(crate) fn set_transform_obj(&self, this_object: SafeHandleObject) { - self.transform_obj.set(*this_object); + if let TransformerType::Js { transform_obj, .. } = &self.transformer_type { + transform_obj.set(*this_object) + } else { + unreachable!("Non-Js transformer type should not set transform_obj") + } } pub(crate) fn set_stream(&self, stream: &TransformStream) { @@ -160,41 +191,71 @@ impl TransformStreamDefaultController { chunk: SafeHandleValue, can_gc: CanGc, ) -> Fallible> { - // If transformerDict["transform"] exists, set transformAlgorithm to an algorithm which - // takes an argument chunk and returns the result of invoking transformerDict["transform"] with argument list - // « chunk, controller » and callback this value transformer. - let algo = self.transform.borrow().clone(); - let result = if let Some(transform) = algo { - rooted!(in(*cx) let this_object = self.transform_obj.get()); - let call_result = transform.Call_( - &this_object.handle(), - chunk, - self, - ExceptionHandling::Rethrow, - can_gc, - ); - match call_result { - Ok(p) => p, - Err(e) => { - let p = Promise::new(global, can_gc); - p.reject_error(e, can_gc); - p - }, - } - } else { - // Let transformAlgorithm be the following steps, taking a chunk argument: - // Let result be TransformStreamDefaultControllerEnqueue(controller, chunk). - // If result is an abrupt completion, return a promise rejected with result.[[Value]]. - let promise = if let Err(error) = self.enqueue(cx, global, chunk, can_gc) { - rooted!(in(*cx) let mut error_val = UndefinedValue()); - error.to_jsval(cx, global, error_val.handle_mut(), can_gc); - Promise::new_rejected(global, cx, error_val.handle(), can_gc) - } else { - // Otherwise, return a promise resolved with undefined. - Promise::new_resolved(global, cx, (), can_gc) - }; - - promise + let result = match &self.transformer_type { + // + TransformerType::Js { + transform, + transform_obj, + .. + } => { + // Step 5. If transformerDict["transform"] exists, set + // transformAlgorithm to an algorithm which takes an argument + // chunk and returns the result of invoking + // transformerDict["transform"] with argument list « chunk, + // controller » and callback this value transformer. + let algo = transform.borrow().clone(); + if let Some(transform) = algo { + rooted!(in(*cx) let this_object = transform_obj.get()); + transform + .Call_( + &this_object.handle(), + chunk, + self, + ExceptionHandling::Rethrow, + can_gc, + ) + .unwrap_or_else(|e| { + let p = Promise::new(global, can_gc); + p.reject_error(e, can_gc); + p + }) + } else { + // Step 2. Let transformAlgorithm be the following steps, taking a chunk argument: + // Let result be TransformStreamDefaultControllerEnqueue(controller, chunk). + // If result is an abrupt completion, return a promise rejected with result.[[Value]]. + if let Err(error) = self.enqueue(cx, global, chunk, can_gc) { + rooted!(in(*cx) let mut error_val = UndefinedValue()); + error.to_jsval(cx, global, error_val.handle_mut(), can_gc); + Promise::new_rejected(global, cx, error_val.handle(), can_gc) + } else { + // Otherwise, return a promise resolved with undefined. + Promise::new_resolved(global, cx, (), can_gc) + } + } + }, + TransformerType::Decoder(decoder) => { + // + // Step 7. Let transformAlgorithm be an algorithm which takes a + // chunk argument and runs the decode and enqueue a chunk + // algorithm with this and chunk. + decode_and_enqueue_a_chunk(cx, global, chunk, decoder, self, can_gc) + // + // Step 5. Let transformAlgorithmWrapper be an algorithm that runs these steps given a value chunk: + // Step 5.1 Let result be the result of running transformAlgorithm given chunk. + // Step 5.2 If result is a Promise, then return result. + // Note: not applicable, the spec does NOT require deode_and_enqueue_a_chunk() to return a Promise + // Step 5.3 Return a promise resolved with undefined. + .map(|_| Promise::new_resolved(global, cx, (), can_gc)) + .unwrap_or_else(|e| { + // + // Step 5.1 If this throws an exception e, + let realm = enter_realm(self); + let p = Promise::new_in_current_realm((&realm).into(), can_gc); + // return a promise rejected with e. + p.reject_error(e, can_gc); + p + }) + }, }; Ok(result) @@ -207,29 +268,49 @@ impl TransformStreamDefaultController { chunk: SafeHandleValue, can_gc: CanGc, ) -> Fallible> { - // If transformerDict["cancel"] exists, set cancelAlgorithm to an algorithm which takes an argument - // reason and returns the result of invoking transformerDict["cancel"] with argument list « reason » - // and callback this value transformer. - let algo = self.cancel.borrow().clone(); - let result = if let Some(cancel) = algo { - rooted!(in(*cx) let this_object = self.transform_obj.get()); - let call_result = cancel.Call_( - &this_object.handle(), - chunk, - ExceptionHandling::Rethrow, - can_gc, - ); - match call_result { - Ok(p) => p, - Err(e) => { - let p = Promise::new(global, can_gc); - p.reject_error(e, can_gc); - p - }, - } - } else { - // Let cancelAlgorithm be an algorithm which returns a promise resolved with undefined. - Promise::new_resolved(global, cx, (), can_gc) + let result = match &self.transformer_type { + // + TransformerType::Js { + cancel, + transform_obj, + .. + } => { + // Step 7. If transformerDict["cancel"] exists, set + // cancelAlgorithm to an algorithm which takes an argument + // reason and returns the result of invoking + // transformerDict["cancel"] with argument list « reason » and + // callback this value transformer. + let algo = cancel.borrow().clone(); + if let Some(cancel) = algo { + rooted!(in(*cx) let this_object = transform_obj.get()); + cancel + .Call_( + &this_object.handle(), + chunk, + ExceptionHandling::Rethrow, + can_gc, + ) + .unwrap_or_else(|e| { + let p = Promise::new(global, can_gc); + p.reject_error(e, can_gc); + p + }) + } else { + // Step 4. Let cancelAlgorithm be an algorithm which returns a promise resolved with undefined. + Promise::new_resolved(global, cx, (), can_gc) + } + }, + TransformerType::Decoder(_) => { + // + // Step 7. Let cancelAlgorithmWrapper be an algorithm that runs these steps given a value reason: + // Step 7.1 Let result be the result of running cancelAlgorithm given reason, + // if cancelAlgorithm was given, or null otherwise + // Note: `TextDecoderStream` does NOT specify a cancel algorithm. + // Step 7.2 If result is a Promise, then return result. + // Note: Not applicable. + // Step 7.3 Return a promise resolved with undefined. + Promise::new_resolved(global, cx, (), can_gc) + }, }; Ok(result) @@ -241,28 +322,60 @@ impl TransformStreamDefaultController { global: &GlobalScope, can_gc: CanGc, ) -> Fallible> { - // If transformerDict["flush"] exists, set flushAlgorithm to an algorithm which returns the result of - // invoking transformerDict["flush"] with argument list « controller » and callback this value transformer. - let algo = self.flush.borrow().clone(); - let result = if let Some(flush) = algo { - rooted!(in(*cx) let this_object = self.transform_obj.get()); - let call_result = flush.Call_( - &this_object.handle(), - self, - ExceptionHandling::Rethrow, - can_gc, - ); - match call_result { - Ok(p) => p, - Err(e) => { - let p = Promise::new(global, can_gc); - p.reject_error(e, can_gc); - p - }, - } - } else { - // Let flushAlgorithm be an algorithm which returns a promise resolved with undefined. - Promise::new_resolved(global, cx, (), can_gc) + let result = match &self.transformer_type { + // + TransformerType::Js { + flush, + transform_obj, + .. + } => { + // Step 6. If transformerDict["flush"] exists, set flushAlgorithm to an + // algorithm which returns the result of invoking + // transformerDict["flush"] with argument list « controller » + // and callback this value transformer. + let algo = flush.borrow().clone(); + if let Some(flush) = algo { + rooted!(in(*cx) let this_object = transform_obj.get()); + flush + .Call_( + &this_object.handle(), + self, + ExceptionHandling::Rethrow, + can_gc, + ) + .unwrap_or_else(|e| { + let p = Promise::new(global, can_gc); + p.reject_error(e, can_gc); + p + }) + } else { + // Step 3. Let flushAlgorithm be an algorithm which returns a promise resolved with undefined. + Promise::new_resolved(global, cx, (), can_gc) + } + }, + TransformerType::Decoder(decoder) => { + // + // Step 8. Let flushAlgorithm be an algorithm which takes no + // arguments and runs the flush and enqueue algorithm with this. + flush_and_enqueue(cx, global, decoder, self, can_gc) + // + // Step 6. Let flushAlgorithmWrapper be an algorithm that runs these steps: + // Step 6.1 Let result be the result of running flushAlgorithm, + // if flushAlgorithm was given, or null otherwise. + // Step 6.2 If result is a Promise, then return result. + // Note: Not applicable. The spec does NOT require flush_and_enqueue algo to return a Promise + // Step 6.3 Return a promise resolved with undefined. + .map(|_| Promise::new_resolved(global, cx, (), can_gc)) + .unwrap_or_else(|e| { + // + // Step 6.1 If this throws an exception e, + let realm = enter_realm(self); + let p = Promise::new_in_current_realm((&realm).into(), can_gc); + // return a promise rejected with e. + p.reject_error(e, can_gc); + p + }) + }, }; Ok(result) @@ -349,14 +462,22 @@ impl TransformStreamDefaultController { /// pub(crate) fn clear_algorithms(&self) { - // Set controller.[[transformAlgorithm]] to undefined. - self.transform.replace(None); + if let TransformerType::Js { + cancel, + flush, + transform, + .. + } = &self.transformer_type + { + // Set controller.[[transformAlgorithm]] to undefined. + transform.replace(None); - // Set controller.[[flushAlgorithm]] to undefined. - self.flush.replace(None); + // Set controller.[[flushAlgorithm]] to undefined. + flush.replace(None); - // Set controller.[[cancelAlgorithm]] to undefined. - self.cancel.replace(None); + // Set controller.[[cancelAlgorithm]] to undefined. + cancel.replace(None); + } } /// @@ -381,6 +502,7 @@ impl TransformStreamDefaultController { } } +#[allow(non_snake_case)] impl TransformStreamDefaultControllerMethods for TransformStreamDefaultController { diff --git a/components/script_bindings/webidls/GenericTransformStream.webidl b/components/script_bindings/webidls/GenericTransformStream.webidl new file mode 100644 index 00000000000..a73b7b465e7 --- /dev/null +++ b/components/script_bindings/webidls/GenericTransformStream.webidl @@ -0,0 +1,9 @@ +/* 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/. */ + +// https://streams.spec.whatwg.org/#generictransformstream +interface mixin GenericTransformStream { + readonly attribute ReadableStream readable; + readonly attribute WritableStream writable; +}; diff --git a/components/script_bindings/webidls/TextDecoder.webidl b/components/script_bindings/webidls/TextDecoder.webidl index 0cf8694efde..6493b0d0ee5 100644 --- a/components/script_bindings/webidls/TextDecoder.webidl +++ b/components/script_bindings/webidls/TextDecoder.webidl @@ -12,7 +12,7 @@ dictionary TextDecodeOptions { boolean stream = false; }; -[Exposed=*] +[Exposed=(Window,Worker)] interface TextDecoder { [Throws] constructor(optional DOMString label = "utf-8", optional TextDecoderOptions options = {}); diff --git a/components/script_bindings/webidls/TextDecoderStream.webidl b/components/script_bindings/webidls/TextDecoderStream.webidl new file mode 100644 index 00000000000..dd012184c3a --- /dev/null +++ b/components/script_bindings/webidls/TextDecoderStream.webidl @@ -0,0 +1,15 @@ +/* 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/. */ + +/* + * For more information on this interface please see + * https://encoding.spec.whatwg.org/#textdecoderstream + */ + +[Exposed=*] +interface TextDecoderStream { + [Throws] constructor(optional DOMString label = "utf-8", optional TextDecoderOptions options = {}); +}; +TextDecoderStream includes TextDecoderCommon; +TextDecoderStream includes GenericTransformStream; diff --git a/tests/wpt/meta/encoding/idlharness.any.js.ini b/tests/wpt/meta/encoding/idlharness.any.js.ini index a1b9cc9ac49..d4038395869 100644 --- a/tests/wpt/meta/encoding/idlharness.any.js.ini +++ b/tests/wpt/meta/encoding/idlharness.any.js.ini @@ -5,33 +5,6 @@ expected: ERROR [idlharness.any.html] - [TextDecoderStream interface: existence and properties of interface object] - expected: FAIL - - [TextDecoderStream interface object length] - expected: FAIL - - [TextDecoderStream interface object name] - expected: FAIL - - [TextDecoderStream interface: existence and properties of interface prototype object] - expected: FAIL - - [TextDecoderStream interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [TextDecoderStream interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [TextDecoderStream interface: attribute encoding] - expected: FAIL - - [TextDecoderStream interface: attribute fatal] - expected: FAIL - - [TextDecoderStream interface: attribute ignoreBOM] - expected: FAIL - [TextEncoderStream interface: existence and properties of interface object] expected: FAIL @@ -55,33 +28,6 @@ [idlharness.any.worker.html] - [TextDecoderStream interface: existence and properties of interface object] - expected: FAIL - - [TextDecoderStream interface object length] - expected: FAIL - - [TextDecoderStream interface object name] - expected: FAIL - - [TextDecoderStream interface: existence and properties of interface prototype object] - expected: FAIL - - [TextDecoderStream interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [TextDecoderStream interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [TextDecoderStream interface: attribute encoding] - expected: FAIL - - [TextDecoderStream interface: attribute fatal] - expected: FAIL - - [TextDecoderStream interface: attribute ignoreBOM] - expected: FAIL - [TextEncoderStream interface: existence and properties of interface object] expected: FAIL diff --git a/tests/wpt/meta/encoding/legacy-mb-schinese/gb18030/gb18030-decoder.any.js.ini b/tests/wpt/meta/encoding/legacy-mb-schinese/gb18030/gb18030-decoder.any.js.ini deleted file mode 100644 index 7fc43a8d82f..00000000000 --- a/tests/wpt/meta/encoding/legacy-mb-schinese/gb18030/gb18030-decoder.any.js.ini +++ /dev/null @@ -1,8 +0,0 @@ -[gb18030-decoder.any.worker.html] - [gb18030 decoder: two bytes 0xFE 0xFF] - expected: FAIL - - -[gb18030-decoder.any.html] - [gb18030 decoder: two bytes 0xFE 0xFF] - expected: FAIL diff --git a/tests/wpt/meta/encoding/streams/backpressure.any.js.ini b/tests/wpt/meta/encoding/streams/backpressure.any.js.ini index e25a1fd37e3..deb5e1b6dd3 100644 --- a/tests/wpt/meta/encoding/streams/backpressure.any.js.ini +++ b/tests/wpt/meta/encoding/streams/backpressure.any.js.ini @@ -1,10 +1,4 @@ [backpressure.any.worker.html] - [write() should not complete until read relieves backpressure for TextDecoderStream] - expected: FAIL - - [additional writes should wait for backpressure to be relieved for class TextDecoderStream] - expected: FAIL - [write() should not complete until read relieves backpressure for TextEncoderStream] expected: FAIL @@ -19,12 +13,6 @@ expected: ERROR [backpressure.any.html] - [write() should not complete until read relieves backpressure for TextDecoderStream] - expected: FAIL - - [additional writes should wait for backpressure to be relieved for class TextDecoderStream] - expected: FAIL - [write() should not complete until read relieves backpressure for TextEncoderStream] expected: FAIL diff --git a/tests/wpt/meta/encoding/streams/decode-attributes.any.js.ini b/tests/wpt/meta/encoding/streams/decode-attributes.any.js.ini index a658dbd9507..48ef17a2366 100644 --- a/tests/wpt/meta/encoding/streams/decode-attributes.any.js.ini +++ b/tests/wpt/meta/encoding/streams/decode-attributes.any.js.ini @@ -2,179 +2,11 @@ expected: ERROR [decode-attributes.any.html] - [encoding attribute should have correct value for 'unicode-1-1-utf-8'] - expected: FAIL - - [encoding attribute should have correct value for 'iso-8859-2'] - expected: FAIL - - [encoding attribute should have correct value for 'ascii'] - expected: FAIL - - [encoding attribute should have correct value for 'utf-16'] - expected: FAIL - - [setting fatal to 'false' should set the attribute to false] - expected: FAIL - - [setting ignoreBOM to 'false' should set the attribute to false] - expected: FAIL - - [setting fatal to '0' should set the attribute to false] - expected: FAIL - - [setting ignoreBOM to '0' should set the attribute to false] - expected: FAIL - - [setting fatal to '' should set the attribute to false] - expected: FAIL - - [setting ignoreBOM to '' should set the attribute to false] - expected: FAIL - - [setting fatal to 'undefined' should set the attribute to false] - expected: FAIL - - [setting ignoreBOM to 'undefined' should set the attribute to false] - expected: FAIL - - [setting fatal to 'null' should set the attribute to false] - expected: FAIL - - [setting ignoreBOM to 'null' should set the attribute to false] - expected: FAIL - - [setting fatal to 'true' should set the attribute to true] - expected: FAIL - - [setting ignoreBOM to 'true' should set the attribute to true] - expected: FAIL - - [setting fatal to '1' should set the attribute to true] - expected: FAIL - - [setting ignoreBOM to '1' should set the attribute to true] - expected: FAIL - - [setting fatal to '[object Object\]' should set the attribute to true] - expected: FAIL - - [setting ignoreBOM to '[object Object\]' should set the attribute to true] - expected: FAIL - - [setting fatal to '' should set the attribute to true] - expected: FAIL - - [setting ignoreBOM to '' should set the attribute to true] - expected: FAIL - - [setting fatal to 'yes' should set the attribute to true] - expected: FAIL - - [setting ignoreBOM to 'yes' should set the attribute to true] - expected: FAIL - - [constructing with an invalid encoding should throw] - expected: FAIL - - [constructing with a non-stringifiable encoding should throw] - expected: FAIL - - [a throwing fatal member should cause the constructor to throw] - expected: FAIL - - [a throwing ignoreBOM member should cause the constructor to throw] - expected: FAIL - [decode-attributes.any.serviceworker.html] expected: ERROR [decode-attributes.any.worker.html] - [encoding attribute should have correct value for 'unicode-1-1-utf-8'] - expected: FAIL - - [encoding attribute should have correct value for 'iso-8859-2'] - expected: FAIL - - [encoding attribute should have correct value for 'ascii'] - expected: FAIL - - [encoding attribute should have correct value for 'utf-16'] - expected: FAIL - - [setting fatal to 'false' should set the attribute to false] - expected: FAIL - - [setting ignoreBOM to 'false' should set the attribute to false] - expected: FAIL - - [setting fatal to '0' should set the attribute to false] - expected: FAIL - - [setting ignoreBOM to '0' should set the attribute to false] - expected: FAIL - - [setting fatal to '' should set the attribute to false] - expected: FAIL - - [setting ignoreBOM to '' should set the attribute to false] - expected: FAIL - - [setting fatal to 'undefined' should set the attribute to false] - expected: FAIL - - [setting ignoreBOM to 'undefined' should set the attribute to false] - expected: FAIL - - [setting fatal to 'null' should set the attribute to false] - expected: FAIL - - [setting ignoreBOM to 'null' should set the attribute to false] - expected: FAIL - - [setting fatal to 'true' should set the attribute to true] - expected: FAIL - - [setting ignoreBOM to 'true' should set the attribute to true] - expected: FAIL - - [setting fatal to '1' should set the attribute to true] - expected: FAIL - - [setting ignoreBOM to '1' should set the attribute to true] - expected: FAIL - - [setting fatal to '[object Object\]' should set the attribute to true] - expected: FAIL - - [setting ignoreBOM to '[object Object\]' should set the attribute to true] - expected: FAIL - - [setting fatal to '' should set the attribute to true] - expected: FAIL - - [setting ignoreBOM to '' should set the attribute to true] - expected: FAIL - - [setting fatal to 'yes' should set the attribute to true] - expected: FAIL - - [setting ignoreBOM to 'yes' should set the attribute to true] - expected: FAIL - - [constructing with an invalid encoding should throw] - expected: FAIL - - [constructing with a non-stringifiable encoding should throw] - expected: FAIL - - [a throwing fatal member should cause the constructor to throw] - expected: FAIL - - [a throwing ignoreBOM member should cause the constructor to throw] - expected: FAIL - [decode-attributes.any.shadowrealm.html] expected: TIMEOUT diff --git a/tests/wpt/meta/encoding/streams/decode-bad-chunks.any.js.ini b/tests/wpt/meta/encoding/streams/decode-bad-chunks.any.js.ini index e88d7611bbd..0af8fb51c5f 100644 --- a/tests/wpt/meta/encoding/streams/decode-bad-chunks.any.js.ini +++ b/tests/wpt/meta/encoding/streams/decode-bad-chunks.any.js.ini @@ -1,36 +1,6 @@ [decode-bad-chunks.any.html] - [chunk of type undefined should error the stream] - expected: FAIL - - [chunk of type null should error the stream] - expected: FAIL - - [chunk of type numeric should error the stream] - expected: FAIL - - [chunk of type object, not BufferSource should error the stream] - expected: FAIL - - [chunk of type array should error the stream] - expected: FAIL - [decode-bad-chunks.any.worker.html] - [chunk of type undefined should error the stream] - expected: FAIL - - [chunk of type null should error the stream] - expected: FAIL - - [chunk of type numeric should error the stream] - expected: FAIL - - [chunk of type object, not BufferSource should error the stream] - expected: FAIL - - [chunk of type array should error the stream] - expected: FAIL - [decode-bad-chunks.any.sharedworker.html] expected: ERROR diff --git a/tests/wpt/meta/encoding/streams/decode-ignore-bom.any.js.ini b/tests/wpt/meta/encoding/streams/decode-ignore-bom.any.js.ini index 6442df2a930..96fd04ddd24 100644 --- a/tests/wpt/meta/encoding/streams/decode-ignore-bom.any.js.ini +++ b/tests/wpt/meta/encoding/streams/decode-ignore-bom.any.js.ini @@ -2,79 +2,8 @@ expected: ERROR [decode-ignore-bom.any.html] - [ignoreBOM should work for encoding utf-8, split at character 0] - expected: FAIL - - [ignoreBOM should work for encoding utf-8, split at character 1] - expected: FAIL - - [ignoreBOM should work for encoding utf-8, split at character 2] - expected: FAIL - - [ignoreBOM should work for encoding utf-8, split at character 3] - expected: FAIL - - [ignoreBOM should work for encoding utf-16le, split at character 0] - expected: FAIL - - [ignoreBOM should work for encoding utf-16le, split at character 1] - expected: FAIL - - [ignoreBOM should work for encoding utf-16le, split at character 2] - expected: FAIL - - [ignoreBOM should work for encoding utf-16le, split at character 3] - expected: FAIL - - [ignoreBOM should work for encoding utf-16be, split at character 0] - expected: FAIL - - [ignoreBOM should work for encoding utf-16be, split at character 1] - expected: FAIL - - [ignoreBOM should work for encoding utf-16be, split at character 2] - expected: FAIL - - [ignoreBOM should work for encoding utf-16be, split at character 3] - expected: FAIL - [decode-ignore-bom.any.serviceworker.html] expected: ERROR [decode-ignore-bom.any.worker.html] - [ignoreBOM should work for encoding utf-8, split at character 0] - expected: FAIL - - [ignoreBOM should work for encoding utf-8, split at character 1] - expected: FAIL - - [ignoreBOM should work for encoding utf-8, split at character 2] - expected: FAIL - - [ignoreBOM should work for encoding utf-8, split at character 3] - expected: FAIL - - [ignoreBOM should work for encoding utf-16le, split at character 0] - expected: FAIL - - [ignoreBOM should work for encoding utf-16le, split at character 1] - expected: FAIL - - [ignoreBOM should work for encoding utf-16le, split at character 2] - expected: FAIL - - [ignoreBOM should work for encoding utf-16le, split at character 3] - expected: FAIL - - [ignoreBOM should work for encoding utf-16be, split at character 0] - expected: FAIL - - [ignoreBOM should work for encoding utf-16be, split at character 1] - expected: FAIL - - [ignoreBOM should work for encoding utf-16be, split at character 2] - expected: FAIL - - [ignoreBOM should work for encoding utf-16be, split at character 3] - expected: FAIL diff --git a/tests/wpt/meta/encoding/streams/decode-incomplete-input.any.js.ini b/tests/wpt/meta/encoding/streams/decode-incomplete-input.any.js.ini index 7006f4bbdd5..d09531b8739 100644 --- a/tests/wpt/meta/encoding/streams/decode-incomplete-input.any.js.ini +++ b/tests/wpt/meta/encoding/streams/decode-incomplete-input.any.js.ini @@ -1,21 +1,9 @@ [decode-incomplete-input.any.html] - [incomplete input with error mode "replacement" should end with a replacement character] - expected: FAIL - - [incomplete input with error mode "fatal" should error the stream] - expected: FAIL - [decode-incomplete-input.any.sharedworker.html] expected: ERROR [decode-incomplete-input.any.worker.html] - [incomplete input with error mode "replacement" should end with a replacement character] - expected: FAIL - - [incomplete input with error mode "fatal" should error the stream] - expected: FAIL - [decode-incomplete-input.any.serviceworker.html] expected: ERROR diff --git a/tests/wpt/meta/encoding/streams/decode-non-utf8.any.js.ini b/tests/wpt/meta/encoding/streams/decode-non-utf8.any.js.ini index 632002e4c39..e22632e2a4c 100644 --- a/tests/wpt/meta/encoding/streams/decode-non-utf8.any.js.ini +++ b/tests/wpt/meta/encoding/streams/decode-non-utf8.any.js.ini @@ -2,86 +2,8 @@ expected: ERROR [decode-non-utf8.any.worker.html] - [TextDecoderStream should be able to reject invalid sequences in Shift_JIS] - expected: FAIL - - [TextDecoderStream should be able to decode invalid sequences in Shift_JIS] - expected: FAIL - - [TextDecoderStream should be able to reject invalid sequences in UTF-16LE] - expected: FAIL - - [TextDecoderStream should be able to reject invalid sequences in UTF-16BE] - expected: FAIL - - [TextDecoderStream should be able to decode invalid sequences in ISO-2022-JP] - expected: FAIL - - [TextDecoderStream should be able to decode ISO-2022-JP] - expected: FAIL - - [TextDecoderStream should be able to reject invalid sequences in ISO-2022-JP] - expected: FAIL - - [TextDecoderStream should be able to decode Shift_JIS] - expected: FAIL - - [TextDecoderStream should be able to decode invalid sequences in UTF-16LE] - expected: FAIL - - [TextDecoderStream should be able to decode UTF-16BE] - expected: FAIL - - [TextDecoderStream should be able to decode invalid sequences in UTF-16BE] - expected: FAIL - - [TextDecoderStream should be able to decode UTF-16LE] - expected: FAIL - - [TextDecoderStream should be able to decode ISO-8859-14] - expected: FAIL - [decode-non-utf8.any.html] - [TextDecoderStream should be able to reject invalid sequences in Shift_JIS] - expected: FAIL - - [TextDecoderStream should be able to decode invalid sequences in Shift_JIS] - expected: FAIL - - [TextDecoderStream should be able to reject invalid sequences in UTF-16LE] - expected: FAIL - - [TextDecoderStream should be able to reject invalid sequences in UTF-16BE] - expected: FAIL - - [TextDecoderStream should be able to decode invalid sequences in ISO-2022-JP] - expected: FAIL - - [TextDecoderStream should be able to decode ISO-2022-JP] - expected: FAIL - - [TextDecoderStream should be able to reject invalid sequences in ISO-2022-JP] - expected: FAIL - - [TextDecoderStream should be able to decode Shift_JIS] - expected: FAIL - - [TextDecoderStream should be able to decode invalid sequences in UTF-16LE] - expected: FAIL - - [TextDecoderStream should be able to decode UTF-16BE] - expected: FAIL - - [TextDecoderStream should be able to decode invalid sequences in UTF-16BE] - expected: FAIL - - [TextDecoderStream should be able to decode UTF-16LE] - expected: FAIL - - [TextDecoderStream should be able to decode ISO-8859-14] - expected: FAIL - [decode-non-utf8.any.serviceworker.html] expected: ERROR diff --git a/tests/wpt/meta/encoding/streams/decode-split-character.any.js.ini b/tests/wpt/meta/encoding/streams/decode-split-character.any.js.ini index 0f8d7c31f56..00b0c368503 100644 --- a/tests/wpt/meta/encoding/streams/decode-split-character.any.js.ini +++ b/tests/wpt/meta/encoding/streams/decode-split-character.any.js.ini @@ -5,70 +5,5 @@ expected: ERROR [decode-split-character.any.worker.html] - [a code point split between chunks should not be emitted until all bytes are available; split point = 2] - expected: FAIL - - [a code point split between chunks should not be emitted until all bytes are available; split point = 3] - expected: FAIL - - [a code point split between chunks should not be emitted until all bytes are available; split point = 4] - expected: FAIL - - [a code point split between chunks should not be emitted until all bytes are available; split point = 5] - expected: FAIL - - [a code point should be emitted as soon as all bytes are available] - expected: FAIL - - [an empty chunk inside a code point split between chunks should not change the output; split point = 1] - expected: FAIL - - [an empty chunk inside a code point split between chunks should not change the output; split point = 2] - expected: FAIL - - [an empty chunk inside a code point split between chunks should not change the output; split point = 3] - expected: FAIL - - [an empty chunk inside a code point split between chunks should not change the output; split point = 4] - expected: FAIL - - [an empty chunk inside a code point split between chunks should not change the output; split point = 5] - expected: FAIL - - [an empty chunk inside a code point split between chunks should not change the output; split point = 6] - expected: FAIL - [decode-split-character.any.html] - [a code point split between chunks should not be emitted until all bytes are available; split point = 2] - expected: FAIL - - [a code point split between chunks should not be emitted until all bytes are available; split point = 3] - expected: FAIL - - [a code point split between chunks should not be emitted until all bytes are available; split point = 4] - expected: FAIL - - [a code point split between chunks should not be emitted until all bytes are available; split point = 5] - expected: FAIL - - [a code point should be emitted as soon as all bytes are available] - expected: FAIL - - [an empty chunk inside a code point split between chunks should not change the output; split point = 1] - expected: FAIL - - [an empty chunk inside a code point split between chunks should not change the output; split point = 2] - expected: FAIL - - [an empty chunk inside a code point split between chunks should not change the output; split point = 3] - expected: FAIL - - [an empty chunk inside a code point split between chunks should not change the output; split point = 4] - expected: FAIL - - [an empty chunk inside a code point split between chunks should not change the output; split point = 5] - expected: FAIL - - [an empty chunk inside a code point split between chunks should not change the output; split point = 6] - expected: FAIL diff --git a/tests/wpt/meta/encoding/streams/decode-utf8.any.js.ini b/tests/wpt/meta/encoding/streams/decode-utf8.any.js.ini index ab141795cbd..54b490024a8 100644 --- a/tests/wpt/meta/encoding/streams/decode-utf8.any.js.ini +++ b/tests/wpt/meta/encoding/streams/decode-utf8.any.js.ini @@ -6,35 +6,6 @@ [decode-utf8.any.worker.html] expected: ERROR - [decoding one UTF-8 chunk should give one output string - ArrayBuffer] - expected: FAIL - - [decoding an empty chunk should give no output chunks - ArrayBuffer] - expected: FAIL - - [an initial empty chunk should be ignored - ArrayBuffer] - expected: FAIL - - [a trailing empty chunk should be ignored - ArrayBuffer] - expected: FAIL - - [UTF-8 EOF handling - ArrayBuffer] - expected: FAIL - [decode-utf8.any.html] expected: ERROR - [decoding one UTF-8 chunk should give one output string - ArrayBuffer] - expected: FAIL - - [decoding an empty chunk should give no output chunks - ArrayBuffer] - expected: FAIL - - [an initial empty chunk should be ignored - ArrayBuffer] - expected: FAIL - - [a trailing empty chunk should be ignored - ArrayBuffer] - expected: FAIL - - [UTF-8 EOF handling - ArrayBuffer] - expected: FAIL diff --git a/tests/wpt/meta/encoding/streams/invalid-realm.window.js.ini b/tests/wpt/meta/encoding/streams/invalid-realm.window.js.ini index 0f16e272af5..08e9f1ab891 100644 --- a/tests/wpt/meta/encoding/streams/invalid-realm.window.js.ini +++ b/tests/wpt/meta/encoding/streams/invalid-realm.window.js.ini @@ -1,12 +1,6 @@ [invalid-realm.window.html] - [TextDecoderStream: write in detached realm should succeed] - expected: FAIL - [TextEncoderStream: write in detached realm should succeed] expected: FAIL [TextEncoderStream: close in detached realm should succeed] expected: FAIL - - [TextDecoderStream: close in detached realm should succeed] - expected: FAIL diff --git a/tests/wpt/meta/encoding/streams/readable-writable-properties.any.js.ini b/tests/wpt/meta/encoding/streams/readable-writable-properties.any.js.ini index 16198f640d4..1f553dbb751 100644 --- a/tests/wpt/meta/encoding/streams/readable-writable-properties.any.js.ini +++ b/tests/wpt/meta/encoding/streams/readable-writable-properties.any.js.ini @@ -2,9 +2,6 @@ [TextEncoderStream readable and writable properties must pass brand checks] expected: FAIL - [TextDecoderStream readable and writable properties must pass brand checks] - expected: FAIL - [readable-writable-properties.any.sharedworker.html] expected: ERROR @@ -16,9 +13,6 @@ [TextEncoderStream readable and writable properties must pass brand checks] expected: FAIL - [TextDecoderStream readable and writable properties must pass brand checks] - expected: FAIL - [readable-writable-properties.any.shadowrealm.html] expected: TIMEOUT diff --git a/tests/wpt/meta/encoding/streams/realms.window.js.ini b/tests/wpt/meta/encoding/streams/realms.window.js.ini index 77ecfc92e5b..9f675afff2c 100644 --- a/tests/wpt/meta/encoding/streams/realms.window.js.ini +++ b/tests/wpt/meta/encoding/streams/realms.window.js.ini @@ -13,24 +13,3 @@ [TypeError for unconvertable chunk should come from constructor realm of TextEncoderStream] expected: FAIL - - [a TextDecoderStream object should be associated with the realm the constructor came from] - expected: FAIL - - [TextDecoderStream's readable and writable attributes should come from the same realm as the constructor definition] - expected: FAIL - - [the result object when read is called after write should come from the same realm as the constructor of TextDecoderStream] - expected: FAIL - - [the result object when write is called with a pending read should come from the same realm as the constructor of TextDecoderStream] - expected: FAIL - - [TypeError for chunk with the wrong type should come from constructor realm of TextDecoderStream] - expected: FAIL - - [TypeError for invalid chunk should come from constructor realm of TextDecoderStream] - expected: FAIL - - [TypeError for incomplete input should come from constructor realm of TextDecoderStream] - expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 98b01860c55..014d6baa7d9 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -13725,14 +13725,14 @@ ] ], "interfaces.https.html": [ - "641c5ba19d389390b7b51da7644f011b0c42f33a", + "efb780c382456b1fda514b33fe5b317c447fa09e", [ null, {} ] ], "interfaces.worker.js": [ - "463bfc25211014203a5094baae4b4e2d890bf9aa", + "a02605ff8e63db701c77d734037fcb33d76418d0", [ "mozilla/interfaces.worker.html", {} diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html index 641c5ba19d3..efb780c3824 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html @@ -326,6 +326,7 @@ test_interfaces([ "TextTrackCueList", "TextTrackList", "TextDecoder", + "TextDecoderStream", "TextEncoder", "TimeRanges", "Touch", diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js index 463bfc25211..a02605ff8e6 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js @@ -110,6 +110,7 @@ test_interfaces([ "SecurityPolicyViolationEvent", "ServiceWorkerContainer", "TextDecoder", + "TextDecoderStream", "TextEncoder", "TrustedHTML", "TrustedScript",