mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +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
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()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue