mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Script: Implement TextDecoderStream
(#38112)
This PR implements the `TextDecoderStream`. Other than introducing the necessary mod and webidl files corresponding to `TextDecoderStream`, this PR also involves some changes in `TextDecoder` and `TrasnformStream`: - The common part that can be shared between `TextDecoder` and `TextDecoderStream` are extracted into a separate type `script::dom::textdecodercommon::TextDecoderCommon`. This type could probably use a different name because there is an interface called `TextDecoderCommon` in the spec (https://encoding.spec.whatwg.org/#textdecodercommon) which just gets included in `TextDecoder` and `TextDecoderStream`. - The three algorithms in `TransformStream` (`cancel`, `flush`, and `transform`) all have become `enum` that has a `Js` variant for a JS function object and a `Native` variant for a rust trait object. Whether the cancel algorithm needs this enum type is debatable as I did not find any interface in the spec that explicitly sets the cancel algorithm. Testing: Existing WPT tests `tests/wpt/tests/encoding/stream` should be sufficient Fixes: #37723 --------- Signed-off-by: minghuaw <michael.wu1107@gmail.com> Signed-off-by: minghuaw <wuminghua7@huawei.com> Signed-off-by: Minghua Wu <michael.wu1107@gmail.com>
This commit is contained in:
parent
25822920cf
commit
554b2da1ad
25 changed files with 797 additions and 752 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textdecoder>
|
||||
#[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<Decoder>,
|
||||
in_stream: RefCell<Vec<u8>>,
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textdecodercommon>
|
||||
decoder: TextDecoderCommon,
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textdecoder-do-not-flush-flag>
|
||||
do_not_flush: Cell<bool>,
|
||||
}
|
||||
|
||||
#[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<crate::DomTypeHolder> for TextDecoder {
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textdecoder>
|
||||
fn Constructor(
|
||||
|
@ -100,85 +93,59 @@ impl TextDecoderMethods<crate::DomTypeHolder> for TextDecoder {
|
|||
))
|
||||
}
|
||||
|
||||
// https://encoding.spec.whatwg.org/#dom-textdecoder-encoding
|
||||
/// <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
|
||||
/// <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
|
||||
/// <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
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textdecoder-decode>
|
||||
fn Decode(
|
||||
&self,
|
||||
input: Option<ArrayBufferViewOrArrayBuffer>,
|
||||
options: &TextDecodeOptions,
|
||||
) -> Fallible<USVString> {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
|
227
components/script/dom/textdecodercommon.rs
Normal file
227
components/script/dom/textdecodercommon.rs
Normal file
|
@ -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.
|
||||
///
|
||||
/// <https://encoding.spec.whatwg.org/#textdecodercommon>
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(JSTraceable, MallocSizeOf)]
|
||||
pub(crate) struct TextDecoderCommon {
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textdecoder-encoding>
|
||||
#[no_trace]
|
||||
encoding: &'static Encoding,
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textdecoder-fatal>
|
||||
fatal: bool,
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textdecoder-ignorebom>
|
||||
ignoreBOM: bool,
|
||||
|
||||
/// The native decoder that is used to perform decoding
|
||||
///
|
||||
/// <https://encoding.spec.whatwg.org/#textdecodercommon-decoder>
|
||||
#[ignore_malloc_size_of = "defined in encoding_rs"]
|
||||
#[no_trace]
|
||||
decoder: RefCell<Decoder>,
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textdecodercommon-i-o-queue>
|
||||
io_queue: RefCell<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[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()),
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textdecoder-encoding>
|
||||
pub(crate) fn encoding(&self) -> &'static Encoding {
|
||||
self.encoding
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textdecodercommon-decoder>
|
||||
pub(crate) fn decoder(&self) -> &RefCell<Decoder> {
|
||||
&self.decoder
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textdecodercommon-i-o-queue>
|
||||
pub(crate) fn io_queue(&self) -> &RefCell<Vec<u8>> {
|
||||
&self.io_queue
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textdecoder-error-mode>
|
||||
pub(crate) fn fatal(&self) -> bool {
|
||||
self.fatal
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textdecoder-ignore-bom-flag>
|
||||
pub(crate) fn ignore_bom(&self) -> bool {
|
||||
self.ignoreBOM
|
||||
}
|
||||
|
||||
/// Shared by `TextDecoder` and `TextDecoderStream`
|
||||
///
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textdecoder-decode>
|
||||
/// <https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk>
|
||||
#[allow(unsafe_code)]
|
||||
pub(crate) fn decode(
|
||||
&self,
|
||||
input: Option<&ArrayBufferViewOrArrayBuffer>,
|
||||
last: bool,
|
||||
) -> Fallible<String> {
|
||||
// <https://encoding.spec.whatwg.org/#dom-textdecoder-decode>
|
||||
// Step 3. If input is given, then push a copy of input to this’s I/O queue.
|
||||
//
|
||||
// <https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk>
|
||||
// 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 {
|
||||
// <https://encoding.spec.whatwg.org/#dom-textdecoder-decode>
|
||||
// Step 4. Let output be the I/O queue of scalar values « end-of-queue ».
|
||||
//
|
||||
// <https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk>
|
||||
// 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`
|
||||
//
|
||||
// <https://encoding.spec.whatwg.org/#dom-textdecoder-decode>
|
||||
// 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.
|
||||
//
|
||||
// <https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk>
|
||||
// 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 {
|
||||
// <https://encoding.spec.whatwg.org/#dom-textdecoder-decode>
|
||||
// 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),
|
||||
// <https://encoding.spec.whatwg.org/#dom-textdecoder-decode>
|
||||
// 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 {
|
||||
// <https://encoding.spec.whatwg.org/#dom-textdecoder-decode>
|
||||
// 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`
|
||||
//
|
||||
// <https://encoding.spec.whatwg.org/#dom-textdecoder-decode>
|
||||
// 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.
|
||||
//
|
||||
// <https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk>
|
||||
// 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 {
|
||||
// <https://encoding.spec.whatwg.org/#dom-textdecoder-decode>
|
||||
// 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)
|
||||
}
|
||||
}
|
204
components/script/dom/textdecoderstream.rs
Normal file
204
components/script/dom/textdecoderstream.rs
Normal file
|
@ -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};
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk>
|
||||
#[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)
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#flush-and-enqueue>
|
||||
#[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)
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textdecoderstream>
|
||||
#[dom_struct]
|
||||
pub(crate) struct TextDecoderStream {
|
||||
reflector_: Reflector,
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#textdecodercommon>
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
decoder: Rc<TextDecoderCommon>,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#generictransformstream>
|
||||
transform: Dom<TransformStream>,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl TextDecoderStream {
|
||||
fn new_inherited(
|
||||
decoder: Rc<TextDecoderCommon>,
|
||||
transform: &TransformStream,
|
||||
) -> TextDecoderStream {
|
||||
TextDecoderStream {
|
||||
reflector_: Reflector::new(),
|
||||
decoder,
|
||||
transform: Dom::from_ref(transform),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_with_proto(
|
||||
cx: SafeJSContext,
|
||||
global: &GlobalScope,
|
||||
proto: Option<SafeHandleObject>,
|
||||
encoding: &'static Encoding,
|
||||
fatal: bool,
|
||||
ignoreBOM: bool,
|
||||
can_gc: CanGc,
|
||||
) -> Fallible<DomRoot<Self>> {
|
||||
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<crate::DomTypeHolder> for TextDecoderStream {
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textdecoderstream>
|
||||
fn Constructor(
|
||||
global: &GlobalScope,
|
||||
proto: Option<SafeHandleObject>,
|
||||
can_gc: CanGc,
|
||||
label: DOMString,
|
||||
options: &TextDecoderBinding::TextDecoderOptions,
|
||||
) -> Fallible<DomRoot<TextDecoderStream>> {
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textdecoder-encoding>
|
||||
fn Encoding(&self) -> DOMString {
|
||||
DOMString::from(self.decoder.encoding().name().to_ascii_lowercase())
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textdecoder-fatal>
|
||||
fn Fatal(&self) -> bool {
|
||||
self.decoder.fatal()
|
||||
}
|
||||
|
||||
/// <https://encoding.spec.whatwg.org/#dom-textdecoder-ignorebom>
|
||||
fn IgnoreBOM(&self) -> bool {
|
||||
self.decoder.ignore_bom()
|
||||
}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#dom-generictransformstream-readable>
|
||||
fn Readable(&self) -> DomRoot<<crate::DomTypeHolder as DomTypes>::ReadableStream> {
|
||||
self.transform.get_readable()
|
||||
}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#dom-generictransformstream-writable>
|
||||
fn Writable(&self) -> DomRoot<<crate::DomTypeHolder as DomTypes>::WritableStream> {
|
||||
self.transform.get_writable()
|
||||
}
|
||||
}
|
|
@ -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
|
||||
/// <https://streams.spec.whatwg.org/#transformstream-set-up>
|
||||
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<TransformStreamDefaultController> {
|
||||
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<crate::DomTypeHolder> for TransformStream {
|
||||
/// <https://streams.spec.whatwg.org/#ts-constructor>
|
||||
#[allow(unsafe_code)]
|
||||
|
|
|
@ -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 {
|
||||
/// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-cancelalgorithm>
|
||||
cancel: RefCell<Option<Rc<TransformerCancelCallback>>>,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-flushalgorithm>
|
||||
flush: RefCell<Option<Rc<TransformerFlushCallback>>>,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-transformalgorithm>
|
||||
transform: RefCell<Option<Rc<TransformerTransformCallback>>>,
|
||||
|
||||
/// The JS object used as `this` when invoking sink algorithms.
|
||||
transform_obj: Heap<*mut JSObject>,
|
||||
},
|
||||
/// Algorithms supporting `TextDecoderStream` are implemented in Rust
|
||||
///
|
||||
/// <https://encoding.spec.whatwg.org/#textdecodercommon>
|
||||
Decoder(Rc<TextDecoderCommon>),
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller>
|
||||
#[dom_struct]
|
||||
pub struct TransformStreamDefaultController {
|
||||
reflector_: Reflector,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-cancelalgorithm>
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
cancel: RefCell<Option<Rc<TransformerCancelCallback>>>,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-flushalgorithm>
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
flush: RefCell<Option<Rc<TransformerFlushCallback>>>,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-transformalgorithm>
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
transform: RefCell<Option<Rc<TransformerTransformCallback>>>,
|
||||
|
||||
/// 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,
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#TransformStreamDefaultController-stream>
|
||||
stream: MutNullableDom<TransformStream>,
|
||||
|
@ -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<TransformStreamDefaultController> {
|
||||
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<Rc<Promise>> {
|
||||
// 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 {
|
||||
// <https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer>
|
||||
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) => {
|
||||
// <https://encoding.spec.whatwg.org/#dom-textdecoderstream>
|
||||
// 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)
|
||||
// <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 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| {
|
||||
// <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)
|
||||
|
@ -207,29 +268,49 @@ impl TransformStreamDefaultController {
|
|||
chunk: SafeHandleValue,
|
||||
can_gc: CanGc,
|
||||
) -> Fallible<Rc<Promise>> {
|
||||
// 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 {
|
||||
// <https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer>
|
||||
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(_) => {
|
||||
// <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)
|
||||
|
@ -241,28 +322,60 @@ impl TransformStreamDefaultController {
|
|||
global: &GlobalScope,
|
||||
can_gc: CanGc,
|
||||
) -> Fallible<Rc<Promise>> {
|
||||
// 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 {
|
||||
// <https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer>
|
||||
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) => {
|
||||
// <https://encoding.spec.whatwg.org/#dom-textdecoderstream>
|
||||
// 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)
|
||||
// <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 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| {
|
||||
// <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)
|
||||
|
@ -349,14 +462,22 @@ impl TransformStreamDefaultController {
|
|||
|
||||
/// <https://streams.spec.whatwg.org/#transform-stream-default-controller-clear-algorithms>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#transform-stream-default-controller-terminate>
|
||||
|
@ -381,6 +502,7 @@ impl TransformStreamDefaultController {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl TransformStreamDefaultControllerMethods<crate::DomTypeHolder>
|
||||
for TransformStreamDefaultController
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue