mirror of
https://github.com/servo/servo.git
synced 2025-09-02 02:58:22 +01:00
Script: Implement TextEncoderStream
(#38466)
This implements the `TextEncoderStream` WebIDL interface. Testing: Existing WPT tests should be sufficient Fixes: #37724 --------- Signed-off-by: minghuaw <wuminghua7@huawei.com>
This commit is contained in:
parent
f24f225db8
commit
d409137e4c
17 changed files with 476 additions and 228 deletions
|
@ -775,6 +775,7 @@ malloc_size_of_is_0!(http::StatusCode);
|
|||
malloc_size_of_is_0!(app_units::Au);
|
||||
malloc_size_of_is_0!(keyboard_types::Modifiers);
|
||||
malloc_size_of_is_0!(mime::Mime);
|
||||
malloc_size_of_is_0!(std::num::NonZeroU16);
|
||||
malloc_size_of_is_0!(std::num::NonZeroU64);
|
||||
malloc_size_of_is_0!(std::num::NonZeroUsize);
|
||||
malloc_size_of_is_0!(std::sync::atomic::AtomicBool);
|
||||
|
|
|
@ -599,6 +599,7 @@ pub(crate) mod textdecoder;
|
|||
pub(crate) mod textdecodercommon;
|
||||
pub(crate) mod textdecoderstream;
|
||||
pub(crate) mod textencoder;
|
||||
pub(crate) mod textencoderstream;
|
||||
pub(crate) mod textmetrics;
|
||||
pub(crate) mod texttrack;
|
||||
pub(crate) mod texttrackcue;
|
||||
|
|
390
components/script/dom/textencoderstream.rs
Normal file
390
components/script/dom/textencoderstream.rs
Normal file
|
@ -0,0 +1,390 @@
|
|||
/* 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::Cell;
|
||||
use std::num::{NonZero, NonZeroU16};
|
||||
use std::ptr::{self, NonNull};
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
use js::conversions::latin1_to_string;
|
||||
use js::jsapi::{
|
||||
JS_DeprecatedStringHasLatin1Chars, JS_GetTwoByteStringCharsAndLength, JS_IsExceptionPending,
|
||||
JSObject, JSType, ToPrimitive,
|
||||
};
|
||||
use js::jsval::UndefinedValue;
|
||||
use js::rust::{
|
||||
HandleObject as SafeHandleObject, HandleValue as SafeHandleValue,
|
||||
MutableHandleValue as SafeMutableHandleValue, ToString,
|
||||
};
|
||||
use js::typedarray::Uint8Array;
|
||||
use script_bindings::conversions::SafeToJSValConvertible;
|
||||
|
||||
use crate::dom::bindings::buffer_source::create_buffer_source;
|
||||
use crate::dom::bindings::codegen::Bindings::TextEncoderStreamBinding::TextEncoderStreamMethods;
|
||||
use crate::dom::bindings::error::{Error, Fallible, throw_dom_exception};
|
||||
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::transformstreamdefaultcontroller::TransformerType;
|
||||
use crate::dom::types::{GlobalScope, TransformStream, TransformStreamDefaultController};
|
||||
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
||||
use crate::{DomTypeHolder, DomTypes};
|
||||
|
||||
/// String converted from an input JS Value
|
||||
enum ConvertedInput<'a> {
|
||||
String(String),
|
||||
CodeUnits(&'a [u16]),
|
||||
}
|
||||
|
||||
/// Converts a JS value to primitive type so that it can be used with
|
||||
/// `ToString`.
|
||||
///
|
||||
/// Set `rval` to `chunk` if `chunk` is a primitive JS value. Otherwise, convert
|
||||
/// `chunk` into a primitive JS value and then set `rval` to the converted
|
||||
/// primitive. This follows the `ToString` procedure with the exception that it
|
||||
/// does not convert the value to string.
|
||||
///
|
||||
/// See below for the `ToString` procedure in spec:
|
||||
/// <https://tc39.es/ecma262/multipage/abstract-operations.html#sec-tostring>
|
||||
#[allow(unsafe_code)]
|
||||
fn jsval_to_primitive(
|
||||
cx: SafeJSContext,
|
||||
global: &GlobalScope,
|
||||
chunk: SafeHandleValue,
|
||||
mut rval: SafeMutableHandleValue,
|
||||
can_gc: CanGc,
|
||||
) -> Fallible<()> {
|
||||
// Step 1. If argument is a String, return argument.
|
||||
// Step 2. If argument is a Symbol, throw a TypeError exception.
|
||||
// Step 3. If argument is undefined, return "undefined".
|
||||
// Step 4. If argument is null, return "null".
|
||||
// Step 5. If argument is true, return "true".
|
||||
// Step 6. If argument is false, return "false".
|
||||
// Step 7. If argument is a Number, return Number::toString(argument, 10).
|
||||
// Step 8. If argument is a BigInt, return BigInt::toString(argument, 10).
|
||||
if chunk.is_primitive() {
|
||||
rval.set(chunk.get());
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Step 9. Assert: argument is an Object.
|
||||
assert!(chunk.is_object());
|
||||
|
||||
// Step 10. Let primValue be ? ToPrimitive(argument, string).
|
||||
rooted!(in(*cx) let obj = chunk.to_object());
|
||||
let is_success =
|
||||
unsafe { ToPrimitive(*cx, obj.handle().into(), JSType::JSTYPE_STRING, rval.into()) };
|
||||
log::debug!("ToPrimitive is_success={:?}", is_success);
|
||||
if !is_success {
|
||||
unsafe {
|
||||
if !JS_IsExceptionPending(*cx) {
|
||||
throw_dom_exception(
|
||||
cx,
|
||||
global,
|
||||
Error::Type("Cannot convert JSObject to primitive".to_owned()),
|
||||
can_gc,
|
||||
);
|
||||
}
|
||||
}
|
||||
return Err(Error::JSFailed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textencoderstream-encoder>
|
||||
#[derive(Default, JSTraceable, MallocSizeOf)]
|
||||
pub(crate) struct Encoder {
|
||||
/// <https://encoding.spec.whatwg.org/#textencoderstream-pending-high-surrogate>
|
||||
leading_surrogate: Cell<Option<NonZeroU16>>,
|
||||
}
|
||||
|
||||
impl Encoder {
|
||||
fn encode(&self, maybe_ill_formed: ConvertedInput<'_>) -> String {
|
||||
match maybe_ill_formed {
|
||||
ConvertedInput::String(s) => {
|
||||
// Rust String is already UTF-8 encoded and cannot contain
|
||||
// surrogate
|
||||
if !s.is_empty() && self.leading_surrogate.take().is_some() {
|
||||
let mut output = String::with_capacity(1 + s.len());
|
||||
output.push('\u{FFFD}');
|
||||
output.push_str(&s);
|
||||
return output;
|
||||
}
|
||||
|
||||
s
|
||||
},
|
||||
ConvertedInput::CodeUnits(code_units) => self.encode_from_code_units(code_units),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an input slice of code unit into unicode scalar values
|
||||
#[allow(unsafe_code)]
|
||||
fn encode_from_code_units(&self, input: &[u16]) -> String {
|
||||
// <https://encoding.spec.whatwg.org/#encode-and-enqueue-a-chunk>
|
||||
//
|
||||
// Step 3. Let output be the I/O queue of bytes « end-of-queue ».
|
||||
let mut output = String::with_capacity(input.len());
|
||||
// Step 4. While true:
|
||||
// Step 4.1 Let item be the result of reading from input.
|
||||
for result in char::decode_utf16(input.iter().cloned()) {
|
||||
// Step 4.3 Let result be the result of executing the convert code unit
|
||||
// to scalar value algorithm with encoder, item and input.
|
||||
|
||||
// <https://encoding.spec.whatwg.org/#convert-code-unit-to-scalar-value>
|
||||
match result {
|
||||
Ok(c) => {
|
||||
// Step 1. If encoder’s leading surrogate is non-null:
|
||||
// Step 1.1 Let leadingSurrogate be encoder’s leading surrogate.
|
||||
// Step 1.2 Set encoder’s leading surrogate to null.
|
||||
if self.leading_surrogate.take().is_some() {
|
||||
// Step 1.5 Return U+FFFD (<28>).
|
||||
output.push('\u{FFFD}');
|
||||
}
|
||||
|
||||
// Step 1.4 Restore item to input.
|
||||
// Note: pushing item to output is equivalent to restoring item to input
|
||||
// and rerun the convert-code-unit-to-scalar-value algo
|
||||
output.push(c);
|
||||
},
|
||||
Err(error) => {
|
||||
let unpaired_surrogate = error.unpaired_surrogate();
|
||||
match code_point_type(unpaired_surrogate) {
|
||||
CodePointType::LeadingSurrogate => {
|
||||
// Step 1.1 If encoder’s leading surrogate is non-null:
|
||||
// Step 1.2 Set encoder’s leading surrogate to null.
|
||||
if self.leading_surrogate.take().is_some() {
|
||||
output.push('\u{FFFD}');
|
||||
}
|
||||
|
||||
// Step 1.4 Restore item to input.
|
||||
// Note: Replacing encoder's leading_surrogate is equivalent
|
||||
// to restore item back to input and rerun the convert-
|
||||
// code-unit-to-scalar-value algo.
|
||||
// Step 2. If item is a leading surrogate, then set encoder’s
|
||||
// leading surrogate to item and return continue.
|
||||
self.leading_surrogate
|
||||
.replace(NonZero::new(unpaired_surrogate));
|
||||
},
|
||||
CodePointType::TrailingSurrogate => match self.leading_surrogate.take() {
|
||||
// Step 1.1 If encoder’s leading surrogate is non-null:
|
||||
// Step 1.2 Set encoder’s leading surrogate to null.
|
||||
Some(leading_surrogate) => {
|
||||
// Step 1.3 If item is a trailing surrogate, then return a scalar
|
||||
// value from surrogates given leadingSurrogate and item.
|
||||
let c = char::decode_utf16([
|
||||
leading_surrogate.get(),
|
||||
unpaired_surrogate,
|
||||
])
|
||||
.next()
|
||||
.expect("A pair of surrogate is supplied")
|
||||
.expect("Decoding a pair of surrogate cannot fail");
|
||||
output.push(c);
|
||||
},
|
||||
// Step 3. If item is a trailing surrogate, then return U+FFFD (<28>).
|
||||
None => output.push('\u{FFFD}'),
|
||||
},
|
||||
CodePointType::ScalarValue => unreachable!("Scalar Value won't fail"),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
enum CodePointType {
|
||||
ScalarValue,
|
||||
LeadingSurrogate,
|
||||
TrailingSurrogate,
|
||||
}
|
||||
|
||||
fn code_point_type(value: u16) -> CodePointType {
|
||||
match value {
|
||||
0xD800..=0xDBFF => CodePointType::LeadingSurrogate,
|
||||
0xDC00..=0xDFFF => CodePointType::TrailingSurrogate,
|
||||
_ => CodePointType::ScalarValue,
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#encode-and-enqueue-a-chunk>
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) fn encode_and_enqueue_a_chunk(
|
||||
cx: SafeJSContext,
|
||||
global: &GlobalScope,
|
||||
chunk: SafeHandleValue,
|
||||
encoder: &Encoder,
|
||||
controller: &TransformStreamDefaultController,
|
||||
can_gc: CanGc,
|
||||
) -> Fallible<()> {
|
||||
// Step 1. Let input be the result of converting chunk to a DOMString.
|
||||
// Step 2. Convert input to an I/O queue of code units.
|
||||
rooted!(in(*cx) let mut rval = UndefinedValue());
|
||||
jsval_to_primitive(cx, global, chunk, rval.handle_mut(), can_gc)?;
|
||||
|
||||
assert!(!rval.is_object());
|
||||
rooted!(in(*cx) let jsstr = unsafe { ToString(*cx, rval.handle()) });
|
||||
if jsstr.is_null() {
|
||||
unsafe {
|
||||
if !JS_IsExceptionPending(*cx) {
|
||||
throw_dom_exception(
|
||||
cx,
|
||||
global,
|
||||
Error::Type("Cannot convert JS primitive to string".to_owned()),
|
||||
can_gc,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(Error::JSFailed);
|
||||
}
|
||||
|
||||
let input = unsafe {
|
||||
if JS_DeprecatedStringHasLatin1Chars(*jsstr) {
|
||||
let s = NonNull::new(*jsstr).expect("jsstr cannot be null");
|
||||
ConvertedInput::String(latin1_to_string(*cx, s))
|
||||
} else {
|
||||
let mut len = 0;
|
||||
let data = JS_GetTwoByteStringCharsAndLength(*cx, std::ptr::null(), *jsstr, &mut len);
|
||||
let maybe_ill_formed_code_units = std::slice::from_raw_parts(data, len);
|
||||
ConvertedInput::CodeUnits(maybe_ill_formed_code_units)
|
||||
}
|
||||
};
|
||||
|
||||
// Step 3. Let output be the I/O queue of bytes « end-of-queue ».
|
||||
// Step 4. While true:
|
||||
// Step 4.1 Let item be the result of reading from input.
|
||||
// Step 4.3 Let result be the result of executing the convert code unit
|
||||
// to scalar value algorithm with encoder, item and input.
|
||||
// Step 4.4 If result is not continue, then process an item with result,
|
||||
// encoder’s encoder, input, output, and "fatal".
|
||||
let output = encoder.encode(input);
|
||||
|
||||
// Step 4.2 If item is end-of-queue:
|
||||
// Step 4.2.1 Convert output into a byte sequence.
|
||||
let output = output.as_bytes();
|
||||
// Step 4.2.2 If output is not empty:
|
||||
if output.is_empty() {
|
||||
// Step 4.2.3
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Step 4.2.2.1 Let chunk be the result of creating a Uint8Array object
|
||||
// given output and encoder’s relevant realm.
|
||||
rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
|
||||
let chunk: Uint8Array = create_buffer_source(cx, output, js_object.handle_mut(), can_gc)
|
||||
.map_err(|_| Error::Type("Cannot convert byte sequence to Uint8Array".to_owned()))?;
|
||||
rooted!(in(*cx) let mut rval = UndefinedValue());
|
||||
chunk.safe_to_jsval(cx, rval.handle_mut());
|
||||
// Step 4.2.2.2 Enqueue chunk into encoder’s transform.
|
||||
controller.enqueue(cx, global, rval.handle(), can_gc)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#encode-and-flush>
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) fn encode_and_flush(
|
||||
cx: SafeJSContext,
|
||||
global: &GlobalScope,
|
||||
encoder: &Encoder,
|
||||
controller: &TransformStreamDefaultController,
|
||||
can_gc: CanGc,
|
||||
) -> Fallible<()> {
|
||||
// Step 1. If encoder’s leading surrogate is non-null:
|
||||
if encoder.leading_surrogate.get().is_some() {
|
||||
// Step 1.1 Let chunk be the result of creating a Uint8Array object
|
||||
// given « 0xEF, 0xBF, 0xBD » and encoder’s relevant realm.
|
||||
rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
|
||||
let chunk: Uint8Array =
|
||||
create_buffer_source(cx, &[0xEF_u8, 0xBF, 0xBD], js_object.handle_mut(), can_gc)
|
||||
.map_err(|_| {
|
||||
Error::Type("Cannot convert byte sequence to Uint8Array".to_owned())
|
||||
})?;
|
||||
rooted!(in(*cx) let mut rval = UndefinedValue());
|
||||
chunk.safe_to_jsval(cx, rval.handle_mut());
|
||||
// Step 1.2 Enqueue chunk into encoder’s transform.
|
||||
return controller.enqueue(cx, global, rval.handle(), can_gc);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textencoderstream>
|
||||
#[dom_struct]
|
||||
pub(crate) struct TextEncoderStream {
|
||||
reflector_: Reflector,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#generictransformstream>
|
||||
transform: Dom<TransformStream>,
|
||||
}
|
||||
|
||||
impl TextEncoderStream {
|
||||
fn new_inherited(transform: &TransformStream) -> TextEncoderStream {
|
||||
Self {
|
||||
reflector_: Reflector::new(),
|
||||
transform: Dom::from_ref(transform),
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textencoderstream>
|
||||
fn new_with_proto(
|
||||
cx: SafeJSContext,
|
||||
global: &GlobalScope,
|
||||
proto: Option<SafeHandleObject>,
|
||||
can_gc: CanGc,
|
||||
) -> Fallible<DomRoot<TextEncoderStream>> {
|
||||
// Step 1. Set this’s encoder to an instance of the UTF-8 encoder.
|
||||
let encoder = Encoder::default();
|
||||
|
||||
// Step 2. Let transformAlgorithm be an algorithm which takes a chunk argument
|
||||
// and runs the encode and enqueue a chunk algorithm with this and chunk.
|
||||
// Step 3. Let flushAlgorithm be an algorithm which runs the encode and flush
|
||||
// algorithm with this.
|
||||
let transformer_type = TransformerType::Encoder(encoder);
|
||||
|
||||
// Step 4. Let transformStream be a new TransformStream.
|
||||
let transform = TransformStream::new_with_proto(global, None, can_gc);
|
||||
// Step 5. Set up transformStream with transformAlgorithm set to transformAlgorithm
|
||||
// and flushAlgorithm set to flushAlgorithm.
|
||||
transform.set_up(cx, global, transformer_type, can_gc)?;
|
||||
|
||||
// Step 6. Set this’s transform to transformStream.
|
||||
Ok(reflect_dom_object_with_proto(
|
||||
Box::new(TextEncoderStream::new_inherited(&transform)),
|
||||
global,
|
||||
proto,
|
||||
can_gc,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl TextEncoderStreamMethods<DomTypeHolder> for TextEncoderStream {
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textencoderstream>
|
||||
fn Constructor(
|
||||
global: &<DomTypeHolder as DomTypes>::GlobalScope,
|
||||
proto: Option<SafeHandleObject>,
|
||||
can_gc: CanGc,
|
||||
) -> Fallible<DomRoot<<DomTypeHolder as DomTypes>::TextEncoderStream>> {
|
||||
TextEncoderStream::new_with_proto(GlobalScope::get_cx(), global, proto, can_gc)
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textencoder-encoding>
|
||||
fn Encoding(&self) -> DOMString {
|
||||
// Returns "utf-8".
|
||||
DOMString::from("utf-8")
|
||||
}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#dom-generictransformstream-readable>
|
||||
fn Readable(&self) -> DomRoot<<DomTypeHolder as DomTypes>::ReadableStream> {
|
||||
self.transform.get_readable()
|
||||
}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#dom-generictransformstream-writable>
|
||||
fn Writable(&self) -> DomRoot<<DomTypeHolder as DomTypes>::WritableStream> {
|
||||
self.transform.get_writable()
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ 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::dom::textencoderstream::{Encoder, encode_and_enqueue_a_chunk, encode_and_flush};
|
||||
use crate::realms::{InRealm, enter_realm};
|
||||
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
||||
|
||||
|
@ -75,6 +76,10 @@ pub(crate) enum TransformerType {
|
|||
///
|
||||
/// <https://encoding.spec.whatwg.org/#textdecodercommon>
|
||||
Decoder(Rc<TextDecoderCommon>),
|
||||
/// Algorithms supporting `TextEncoderStream` are implemented in Rust
|
||||
///
|
||||
/// <https://encoding.spec.whatwg.org/#textencoderstream-encoder>
|
||||
Encoder(Encoder),
|
||||
}
|
||||
|
||||
impl TransformerType {
|
||||
|
@ -256,6 +261,28 @@ impl TransformStreamDefaultController {
|
|||
p
|
||||
})
|
||||
},
|
||||
TransformerType::Encoder(encoder) => {
|
||||
// <https://encoding.spec.whatwg.org/#dom-textencoderstream>
|
||||
// Step 2. Let transformAlgorithm be an algorithm which takes a chunk argument and runs the encode
|
||||
// and enqueue a chunk algorithm with this and chunk.
|
||||
encode_and_enqueue_a_chunk(cx, global, chunk, encoder, self, can_gc)
|
||||
// <https://streams.spec.whatwg.org/#transformstream-set-up>
|
||||
// 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 encode_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| {
|
||||
// <https://streams.spec.whatwg.org/#transformstream-set-up>
|
||||
// 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)
|
||||
|
@ -311,6 +338,17 @@ impl TransformStreamDefaultController {
|
|||
// Step 7.3 Return a promise resolved with undefined.
|
||||
Promise::new_resolved(global, cx, (), can_gc)
|
||||
},
|
||||
TransformerType::Encoder(_) => {
|
||||
// <https://streams.spec.whatwg.org/#transformstream-set-up>
|
||||
// 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)
|
||||
|
@ -376,6 +414,28 @@ impl TransformStreamDefaultController {
|
|||
p
|
||||
})
|
||||
},
|
||||
TransformerType::Encoder(encoder) => {
|
||||
// <https://encoding.spec.whatwg.org/#textencoderstream-encoder>
|
||||
// Step 3. Let flushAlgorithm be an algorithm which runs the encode and flush algorithm with this.
|
||||
encode_and_flush(cx, global, encoder, self, can_gc)
|
||||
// <https://streams.spec.whatwg.org/#transformstream-set-up>
|
||||
// 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 encode_and_flush 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| {
|
||||
// <https://streams.spec.whatwg.org/#transformstream-set-up>
|
||||
// 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)
|
||||
|
|
15
components/script_bindings/webidls/TextEncoderStream.webidl
Normal file
15
components/script_bindings/webidls/TextEncoderStream.webidl
Normal file
|
@ -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/#textencoderstream
|
||||
*/
|
||||
|
||||
[Exposed=*]
|
||||
interface TextEncoderStream {
|
||||
[Throws] constructor();
|
||||
};
|
||||
TextEncoderStream includes TextEncoderCommon;
|
||||
TextEncoderStream includes GenericTransformStream;
|
42
tests/wpt/meta/encoding/idlharness.any.js.ini
vendored
42
tests/wpt/meta/encoding/idlharness.any.js.ini
vendored
|
@ -5,50 +5,8 @@
|
|||
expected: ERROR
|
||||
|
||||
[idlharness.any.html]
|
||||
[TextEncoderStream interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream interface object length]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream interface object name]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream interface: existence and properties of interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream interface: existence and properties of interface prototype object's "constructor" property]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream interface: attribute encoding]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[idlharness.any.worker.html]
|
||||
[TextEncoderStream interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream interface object length]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream interface object name]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream interface: existence and properties of interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream interface: existence and properties of interface prototype object's "constructor" property]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream interface: existence and properties of interface prototype object's @@unscopables property]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream interface: attribute encoding]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[idlharness.any.shadowrealm-in-window.html]
|
||||
expected: ERROR
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
[backpressure.any.worker.html]
|
||||
[write() should not complete until read relieves backpressure for TextEncoderStream]
|
||||
expected: FAIL
|
||||
|
||||
[additional writes should wait for backpressure to be relieved for class TextEncoderStream]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[backpressure.any.sharedworker.html]
|
||||
expected: ERROR
|
||||
|
@ -13,12 +7,6 @@
|
|||
expected: ERROR
|
||||
|
||||
[backpressure.any.html]
|
||||
[write() should not complete until read relieves backpressure for TextEncoderStream]
|
||||
expected: FAIL
|
||||
|
||||
[additional writes should wait for backpressure to be relieved for class TextEncoderStream]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[backpressure.any.shadowrealm.html]
|
||||
expected: TIMEOUT
|
||||
|
|
|
@ -1,22 +1,4 @@
|
|||
[encode-bad-chunks.any.html]
|
||||
[a chunk that cannot be converted to a string should error the streams]
|
||||
expected: FAIL
|
||||
|
||||
[input of type undefined should be converted correctly to string]
|
||||
expected: FAIL
|
||||
|
||||
[input of type null should be converted correctly to string]
|
||||
expected: FAIL
|
||||
|
||||
[input of type numeric should be converted correctly to string]
|
||||
expected: FAIL
|
||||
|
||||
[input of type object should be converted correctly to string]
|
||||
expected: FAIL
|
||||
|
||||
[input of type array should be converted correctly to string]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[encode-bad-chunks.any.sharedworker.html]
|
||||
expected: ERROR
|
||||
|
@ -25,20 +7,3 @@
|
|||
expected: ERROR
|
||||
|
||||
[encode-bad-chunks.any.worker.html]
|
||||
[a chunk that cannot be converted to a string should error the streams]
|
||||
expected: FAIL
|
||||
|
||||
[input of type undefined should be converted correctly to string]
|
||||
expected: FAIL
|
||||
|
||||
[input of type null should be converted correctly to string]
|
||||
expected: FAIL
|
||||
|
||||
[input of type numeric should be converted correctly to string]
|
||||
expected: FAIL
|
||||
|
||||
[input of type object should be converted correctly to string]
|
||||
expected: FAIL
|
||||
|
||||
[input of type array should be converted correctly to string]
|
||||
expected: FAIL
|
||||
|
|
|
@ -2,121 +2,8 @@
|
|||
expected: ERROR
|
||||
|
||||
[encode-utf8.any.html]
|
||||
[encoding one string of UTF-8 should give one complete chunk]
|
||||
expected: FAIL
|
||||
|
||||
[a character split between chunks should be correctly encoded]
|
||||
expected: FAIL
|
||||
|
||||
[a character following one split between chunks should be correctly encoded]
|
||||
expected: FAIL
|
||||
|
||||
[two consecutive astral characters each split down the middle should be correctly reassembled]
|
||||
expected: FAIL
|
||||
|
||||
[two consecutive astral characters each split down the middle with an invalid surrogate in the middle should be correctly encoded]
|
||||
expected: FAIL
|
||||
|
||||
[a stream ending in a leading surrogate should emit a replacement character as a final chunk]
|
||||
expected: FAIL
|
||||
|
||||
[an unmatched surrogate at the end of a chunk followed by an astral character in the next chunk should be replaced with the replacement character at the start of the next output chunk]
|
||||
expected: FAIL
|
||||
|
||||
[an unmatched surrogate at the end of a chunk followed by an ascii character in the next chunk should be replaced with the replacement character at the start of the next output chunk]
|
||||
expected: FAIL
|
||||
|
||||
[an unmatched surrogate at the end of a chunk followed by a plane 1 character split into two chunks should result in the encoded plane 1 character appearing in the last output chunk]
|
||||
expected: FAIL
|
||||
|
||||
[two leading chunks should result in two replacement characters]
|
||||
expected: FAIL
|
||||
|
||||
[a non-terminal unpaired leading surrogate should immediately be replaced]
|
||||
expected: FAIL
|
||||
|
||||
[a terminal unpaired trailing surrogate should immediately be replaced]
|
||||
expected: FAIL
|
||||
|
||||
[a leading surrogate chunk should be carried past empty chunks]
|
||||
expected: FAIL
|
||||
|
||||
[a leading surrogate chunk should error when it is clear it didn't form a pair]
|
||||
expected: FAIL
|
||||
|
||||
[an empty string should result in no output chunk]
|
||||
expected: FAIL
|
||||
|
||||
[a leading empty chunk should be ignored]
|
||||
expected: FAIL
|
||||
|
||||
[a trailing empty chunk should be ignored]
|
||||
expected: FAIL
|
||||
|
||||
[a plain ASCII chunk should be converted]
|
||||
expected: FAIL
|
||||
|
||||
[characters in the ISO-8859-1 range should be encoded correctly]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[encode-utf8.any.sharedworker.html]
|
||||
expected: ERROR
|
||||
|
||||
[encode-utf8.any.worker.html]
|
||||
[encoding one string of UTF-8 should give one complete chunk]
|
||||
expected: FAIL
|
||||
|
||||
[a character split between chunks should be correctly encoded]
|
||||
expected: FAIL
|
||||
|
||||
[a character following one split between chunks should be correctly encoded]
|
||||
expected: FAIL
|
||||
|
||||
[two consecutive astral characters each split down the middle should be correctly reassembled]
|
||||
expected: FAIL
|
||||
|
||||
[two consecutive astral characters each split down the middle with an invalid surrogate in the middle should be correctly encoded]
|
||||
expected: FAIL
|
||||
|
||||
[a stream ending in a leading surrogate should emit a replacement character as a final chunk]
|
||||
expected: FAIL
|
||||
|
||||
[an unmatched surrogate at the end of a chunk followed by an astral character in the next chunk should be replaced with the replacement character at the start of the next output chunk]
|
||||
expected: FAIL
|
||||
|
||||
[an unmatched surrogate at the end of a chunk followed by an ascii character in the next chunk should be replaced with the replacement character at the start of the next output chunk]
|
||||
expected: FAIL
|
||||
|
||||
[an unmatched surrogate at the end of a chunk followed by a plane 1 character split into two chunks should result in the encoded plane 1 character appearing in the last output chunk]
|
||||
expected: FAIL
|
||||
|
||||
[two leading chunks should result in two replacement characters]
|
||||
expected: FAIL
|
||||
|
||||
[a non-terminal unpaired leading surrogate should immediately be replaced]
|
||||
expected: FAIL
|
||||
|
||||
[a terminal unpaired trailing surrogate should immediately be replaced]
|
||||
expected: FAIL
|
||||
|
||||
[a leading surrogate chunk should be carried past empty chunks]
|
||||
expected: FAIL
|
||||
|
||||
[a leading surrogate chunk should error when it is clear it didn't form a pair]
|
||||
expected: FAIL
|
||||
|
||||
[an empty string should result in no output chunk]
|
||||
expected: FAIL
|
||||
|
||||
[a leading empty chunk should be ignored]
|
||||
expected: FAIL
|
||||
|
||||
[a trailing empty chunk should be ignored]
|
||||
expected: FAIL
|
||||
|
||||
[a plain ASCII chunk should be converted]
|
||||
expected: FAIL
|
||||
|
||||
[characters in the ISO-8859-1 range should be encoded correctly]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
[invalid-realm.window.html]
|
||||
[TextEncoderStream: write in detached realm should succeed]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream: close in detached realm should succeed]
|
||||
expected: FAIL
|
|
@ -1,7 +1,4 @@
|
|||
[readable-writable-properties.any.html]
|
||||
[TextEncoderStream readable and writable properties must pass brand checks]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[readable-writable-properties.any.sharedworker.html]
|
||||
expected: ERROR
|
||||
|
@ -10,9 +7,6 @@
|
|||
expected: ERROR
|
||||
|
||||
[readable-writable-properties.any.worker.html]
|
||||
[TextEncoderStream readable and writable properties must pass brand checks]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[readable-writable-properties.any.shadowrealm.html]
|
||||
expected: TIMEOUT
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
[realms.window.html]
|
||||
[a TextEncoderStream object should be associated with the realm the constructor came from]
|
||||
expected: FAIL
|
||||
|
||||
[TextEncoderStream's readable and writable attributes should come from the same realm as the constructor definition]
|
||||
expected: FAIL
|
||||
|
||||
[the output chunks when read is called after write should come from the same realm as the constructor of TextEncoderStream]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[stringification-crash.html]
|
||||
expected: TIMEOUT
|
|
@ -1,15 +1,16 @@
|
|||
[fetch-event.https.h2.html]
|
||||
expected: TIMEOUT
|
||||
[global setup]
|
||||
expected: FAIL
|
||||
|
||||
[The streaming request body is readable in the service worker.]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Network fallback for streaming upload.]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[When the streaming request body is used, network fallback fails.]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
||||
[Running clone() in the service worker does not prevent network fallback.]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
|
4
tests/wpt/mozilla/meta/MANIFEST.json
vendored
4
tests/wpt/mozilla/meta/MANIFEST.json
vendored
|
@ -13725,14 +13725,14 @@
|
|||
]
|
||||
],
|
||||
"interfaces.https.html": [
|
||||
"cbdb5bef0bab4f54514913517c214631a71f7d3a",
|
||||
"40567f81c201fe775ad4735c231349fac8c33381",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
]
|
||||
],
|
||||
"interfaces.worker.js": [
|
||||
"1955d6f821d8445c813ac09e098b2c66f0e07d26",
|
||||
"66f95241d5518a6b82725529775dc5613cbab1c0",
|
||||
[
|
||||
"mozilla/interfaces.worker.html",
|
||||
{}
|
||||
|
|
|
@ -330,6 +330,7 @@ test_interfaces([
|
|||
"TextDecoder",
|
||||
"TextDecoderStream",
|
||||
"TextEncoder",
|
||||
"TextEncoderStream",
|
||||
"TimeRanges",
|
||||
"Touch",
|
||||
"TouchEvent",
|
||||
|
|
|
@ -113,6 +113,7 @@ test_interfaces([
|
|||
"TextDecoder",
|
||||
"TextDecoderStream",
|
||||
"TextEncoder",
|
||||
"TextEncoderStream",
|
||||
"TrustedHTML",
|
||||
"TrustedScript",
|
||||
"TrustedScriptURL",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue