mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
script: Implement Blob::bytes()
(#35151)
* script: Implement Blob.bytes() Signed-off-by: yoseio <98276492+yoseio@users.noreply.github.com> * improve read_all_bytes Signed-off-by: yoseio <98276492+yoseio@users.noreply.github.com> * fix read_all_bytes Signed-off-by: yoseio <98276492+yoseio@users.noreply.github.com> * fix bug Signed-off-by: yoseio <98276492+yoseio@users.noreply.github.com> * something went wrong Signed-off-by: Kousuke Takaki <98276492+yoseio@users.noreply.github.com> * fix read loop Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * add use of can_gc to promise code following rebase Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * fix rooting of fulfillment handler Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * Update test expectations Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com> * use dom for reader in read loop fulfillment handler Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * use the global of the reader in read loop fulfillment handler Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> * remove FAIl expectations for blob methods in detached iframe Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> --------- Signed-off-by: yoseio <98276492+yoseio@users.noreply.github.com> Signed-off-by: Kousuke Takaki <98276492+yoseio@users.noreply.github.com> Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com> Signed-off-by: Taym Haddadi <haddadi.taym@gmail.com> Co-authored-by: gterzian <2792687+gterzian@users.noreply.github.com> Co-authored-by: Taym Haddadi <haddadi.taym@gmail.com>
This commit is contained in:
parent
a1ecce5502
commit
8a3f62933b
9 changed files with 205 additions and 80 deletions
|
@ -4,17 +4,21 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
use std::num::NonZeroU32;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use base::id::{BlobId, BlobIndex, PipelineNamespaceId};
|
||||
use dom_struct::dom_struct;
|
||||
use encoding_rs::UTF_8;
|
||||
use js::jsapi::JSObject;
|
||||
use js::rust::HandleObject;
|
||||
use js::typedarray::Uint8;
|
||||
use net_traits::filemanager_thread::RelativePos;
|
||||
use script_traits::serializable::BlobImpl;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::body::{run_array_buffer_data_algorithm, FetchedData};
|
||||
use crate::dom::bindings::buffer_source::create_buffer_source;
|
||||
use crate::dom::bindings::codegen::Bindings::BlobBinding;
|
||||
use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
|
||||
use crate::dom::bindings::codegen::UnionTypes::ArrayBufferOrArrayBufferViewOrBlobOrString;
|
||||
|
@ -297,6 +301,46 @@ impl BlobMethods<crate::DomTypeHolder> for Blob {
|
|||
);
|
||||
p
|
||||
}
|
||||
|
||||
/// <https://w3c.github.io/FileAPI/#dom-blob-bytes>
|
||||
fn Bytes(&self, in_realm: InRealm, can_gc: CanGc) -> Rc<Promise> {
|
||||
let cx = GlobalScope::get_cx();
|
||||
let global = GlobalScope::from_safe_context(cx, in_realm);
|
||||
let p = Promise::new_in_current_realm(in_realm, can_gc);
|
||||
|
||||
// 1. Let stream be the result of calling get stream on this.
|
||||
let stream = self.get_stream(can_gc);
|
||||
|
||||
// 2. Let reader be the result of getting a reader from stream.
|
||||
// If that threw an exception, return a new promise rejected with that exception.
|
||||
let reader = match stream.and_then(|s| s.acquire_default_reader(can_gc)) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
p.reject_error(e, can_gc);
|
||||
return p;
|
||||
},
|
||||
};
|
||||
|
||||
// 3. Let promise be the result of reading all bytes from stream with reader.
|
||||
let p_success = p.clone();
|
||||
let p_failure = p.clone();
|
||||
reader.read_all_bytes(
|
||||
cx,
|
||||
&global,
|
||||
Rc::new(move |bytes| {
|
||||
rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
|
||||
let arr = create_buffer_source::<Uint8>(cx, bytes, js_object.handle_mut(), can_gc)
|
||||
.expect("Converting input to uint8 array should never fail");
|
||||
p_success.resolve_native(&arr, can_gc);
|
||||
}),
|
||||
Rc::new(move |cx, v| {
|
||||
p_failure.reject(cx, v, can_gc);
|
||||
}),
|
||||
in_realm,
|
||||
can_gc,
|
||||
);
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the normalized, MIME-parsable type string
|
||||
|
|
|
@ -28,11 +28,137 @@ use crate::dom::defaultteereadrequest::DefaultTeeReadRequest;
|
|||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::promise::Promise;
|
||||
use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
|
||||
use crate::dom::readablestream::ReadableStream;
|
||||
use crate::dom::readablestream::{get_read_promise_bytes, get_read_promise_done, ReadableStream};
|
||||
use crate::dom::readablestreamgenericreader::ReadableStreamGenericReader;
|
||||
use crate::realms::{enter_realm, InRealm};
|
||||
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
||||
|
||||
type ReadAllBytesSuccessSteps = dyn Fn(&[u8]);
|
||||
type ReadAllBytesFailureSteps = dyn Fn(SafeJSContext, SafeHandleValue);
|
||||
|
||||
impl js::gc::Rootable for ReadLoopFulFillmentHandler {}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#read-loop>
|
||||
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
||||
struct ReadLoopFulFillmentHandler {
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
#[no_trace]
|
||||
success_steps: Rc<ReadAllBytesSuccessSteps>,
|
||||
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
#[no_trace]
|
||||
failure_steps: Rc<ReadAllBytesFailureSteps>,
|
||||
|
||||
reader: Dom<ReadableStreamDefaultReader>,
|
||||
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
bytes: Rc<DomRefCell<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl Callback for ReadLoopFulFillmentHandler {
|
||||
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||
fn callback(&self, cx: SafeJSContext, v: SafeHandleValue, realm: InRealm, can_gc: CanGc) {
|
||||
let global = self.reader.global();
|
||||
let is_done = match get_read_promise_done(cx, &v) {
|
||||
Ok(is_done) => is_done,
|
||||
Err(err) => {
|
||||
self.reader
|
||||
.release(can_gc)
|
||||
.expect("Releasing the reader should succeed");
|
||||
rooted!(in(*cx) let mut v = UndefinedValue());
|
||||
err.to_jsval(cx, &global, v.handle_mut());
|
||||
(self.failure_steps)(cx, v.handle());
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
if is_done {
|
||||
// <https://streams.spec.whatwg.org/#ref-for-read-request-close-steps%E2%91%A6>
|
||||
// Call successSteps with bytes.
|
||||
(self.success_steps)(&self.bytes.borrow());
|
||||
self.reader
|
||||
.release(can_gc)
|
||||
.expect("Releasing the reader should succeed");
|
||||
} else {
|
||||
// <https://streams.spec.whatwg.org/#ref-for-read-request-chunk-steps%E2%91%A6>
|
||||
let chunk = match get_read_promise_bytes(cx, &v) {
|
||||
Ok(chunk) => chunk,
|
||||
Err(err) => {
|
||||
// If chunk is not a Uint8Array object, call failureSteps with a TypeError and abort these steps.
|
||||
rooted!(in(*cx) let mut v = UndefinedValue());
|
||||
err.to_jsval(cx, &global, v.handle_mut());
|
||||
(self.failure_steps)(cx, v.handle());
|
||||
self.reader
|
||||
.release(can_gc)
|
||||
.expect("Releasing the reader should succeed");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// Append the bytes represented by chunk to bytes.
|
||||
let mut bytes = self.bytes.borrow_mut();
|
||||
bytes.extend_from_slice(&chunk);
|
||||
|
||||
// Read-loop given reader, bytes, successSteps, and failureSteps.
|
||||
read_loop(
|
||||
&global,
|
||||
&mut Some(self.clone()),
|
||||
Box::new(ReadLoopRejectionHandler {
|
||||
failure_steps: self.failure_steps.clone(),
|
||||
}),
|
||||
realm,
|
||||
can_gc,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||
/// <https://streams.spec.whatwg.org/#readablestreamdefaultreader-read-all-bytes>
|
||||
struct ReadLoopRejectionHandler {
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
#[no_trace]
|
||||
failure_steps: Rc<ReadAllBytesFailureSteps>,
|
||||
}
|
||||
|
||||
impl Callback for ReadLoopRejectionHandler {
|
||||
/// <https://streams.spec.whatwg.org/#ref-for-read-request-error-steps%E2%91%A6>
|
||||
fn callback(&self, cx: SafeJSContext, v: SafeHandleValue, _realm: InRealm, _can_gc: CanGc) {
|
||||
// Call failureSteps with e.
|
||||
(self.failure_steps)(cx, v);
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#read-loop>
|
||||
fn read_loop(
|
||||
global: &GlobalScope,
|
||||
fulfillment_handler: &mut Option<ReadLoopFulFillmentHandler>,
|
||||
rejection_handler: Box<ReadLoopRejectionHandler>,
|
||||
realm: InRealm,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
// Let readRequest be a new read request with the following items:
|
||||
// Note: the custom read request logic is implemented
|
||||
// using a native promise handler attached to the promise returned by `Read`
|
||||
// (which internally uses a default read request).
|
||||
|
||||
// Perform ! ReadableStreamDefaultReaderRead(reader, readRequest).
|
||||
let read_promise = fulfillment_handler
|
||||
.as_ref()
|
||||
.expect("Fulfillment handler should be some.")
|
||||
.reader
|
||||
.Read(can_gc);
|
||||
|
||||
let handler = PromiseNativeHandler::new(
|
||||
global,
|
||||
fulfillment_handler.take().map(|h| Box::new(h) as Box<_>),
|
||||
Some(rejection_handler),
|
||||
can_gc,
|
||||
);
|
||||
read_promise.append_native_handler(&handler, realm, can_gc);
|
||||
}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#read-request>
|
||||
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||
pub(crate) enum ReadRequest {
|
||||
|
@ -348,6 +474,37 @@ impl ReadableStreamDefaultReader {
|
|||
.borrow()
|
||||
.append_native_handler(&handler, comp, can_gc);
|
||||
}
|
||||
|
||||
/// <https://streams.spec.whatwg.org/#readablestreamdefaultreader-read-all-bytes>
|
||||
pub(crate) fn read_all_bytes(
|
||||
&self,
|
||||
cx: SafeJSContext,
|
||||
global: &GlobalScope,
|
||||
success_steps: Rc<ReadAllBytesSuccessSteps>,
|
||||
failure_steps: Rc<ReadAllBytesFailureSteps>,
|
||||
realm: InRealm,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
// To read all bytes from a ReadableStreamDefaultReader reader,
|
||||
// given successSteps, which is an algorithm accepting a byte sequence,
|
||||
// and failureSteps, which is an algorithm accepting a JavaScript value:
|
||||
// read-loop given reader, a new byte sequence, successSteps, and failureSteps.
|
||||
// Note: read-loop done using native promise handlers.
|
||||
rooted!(in(*cx) let mut fulfillment_handler = Some(ReadLoopFulFillmentHandler {
|
||||
success_steps,
|
||||
failure_steps: failure_steps.clone(),
|
||||
reader: Dom::from_ref(self),
|
||||
bytes: Rc::new(DomRefCell::new(Vec::new())),
|
||||
}));
|
||||
let rejection_handler = Box::new(ReadLoopRejectionHandler { failure_steps });
|
||||
read_loop(
|
||||
global,
|
||||
&mut fulfillment_handler,
|
||||
rejection_handler,
|
||||
realm,
|
||||
can_gc,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadableStreamDefaultReaderMethods<crate::DomTypeHolder> for ReadableStreamDefaultReader {
|
||||
|
|
|
@ -34,7 +34,8 @@ DOMInterfaces = {
|
|||
|
||||
'Blob': {
|
||||
'weakReferenceable': True,
|
||||
'canGc': ['Slice', 'Text', 'ArrayBuffer', 'Stream'],
|
||||
'canGc': ['Slice', 'Text', 'ArrayBuffer', 'Stream', 'Bytes'],
|
||||
'inRealms': ['Bytes'],
|
||||
},
|
||||
|
||||
'Bluetooth': {
|
||||
|
|
|
@ -20,6 +20,7 @@ interface Blob {
|
|||
[NewObject, Throws] ReadableStream stream();
|
||||
[NewObject] Promise<DOMString> text();
|
||||
[NewObject] Promise<ArrayBuffer> arrayBuffer();
|
||||
[NewObject] Promise<Uint8Array> bytes();
|
||||
};
|
||||
|
||||
dictionary BlobPropertyBag {
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
[Blob-methods-from-detached-frame.html]
|
||||
[slice()]
|
||||
expected: FAIL
|
||||
|
||||
[bytes()]
|
||||
expected: FAIL
|
|
@ -1,32 +0,0 @@
|
|||
[Blob-bytes.any.html]
|
||||
[Blob.bytes()]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.bytes() empty Blob data]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.bytes() non-ascii input]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.bytes() non-unicode input]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.bytes() concurrent reads]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[Blob-bytes.any.worker.html]
|
||||
[Blob.bytes()]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.bytes() empty Blob data]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.bytes() non-ascii input]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.bytes() non-unicode input]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.bytes() concurrent reads]
|
||||
expected: FAIL
|
26
tests/wpt/meta/FileAPI/idlharness.any.js.ini
vendored
26
tests/wpt/meta/FileAPI/idlharness.any.js.ini
vendored
|
@ -1,8 +1,4 @@
|
|||
[idlharness.any.html]
|
||||
[Blob interface: operation text()]
|
||||
|
||||
[Blob interface: operation arrayBuffer()]
|
||||
|
||||
[FileReader interface: operation readAsBinaryString(Blob)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -12,21 +8,8 @@
|
|||
[FileReader interface: calling readAsBinaryString(Blob) on new FileReader() with too few arguments must throw TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: operation bytes()]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new Blob(["TEST"\]) must inherit property "bytes()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "bytes()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[idlharness.any.worker.html]
|
||||
[Blob interface: operation text()]
|
||||
|
||||
[Blob interface: operation arrayBuffer()]
|
||||
|
||||
[FileReader interface: operation readAsBinaryString(Blob)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -35,12 +18,3 @@
|
|||
|
||||
[FileReader interface: calling readAsBinaryString(Blob) on new FileReader() with too few arguments must throw TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: operation bytes()]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new Blob(["TEST"\]) must inherit property "bytes()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "bytes()" with the proper type]
|
||||
expected: FAIL
|
||||
|
|
7
tests/wpt/meta/FileAPI/idlharness.html.ini
vendored
7
tests/wpt/meta/FileAPI/idlharness.html.ini
vendored
|
@ -1,8 +1,4 @@
|
|||
[idlharness.html]
|
||||
[Blob interface: operation text()]
|
||||
|
||||
[Blob interface: operation arrayBuffer()]
|
||||
|
||||
[FileReader interface: operation readAsBinaryString(Blob)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -17,6 +13,3 @@
|
|||
|
||||
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "stream()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: operation bytes()]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
[idlharness.worker.html]
|
||||
[Blob interface: operation text()]
|
||||
|
||||
[Blob interface: operation arrayBuffer()]
|
||||
|
||||
[FileReader interface: operation readAsBinaryString(Blob)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -17,6 +13,3 @@
|
|||
|
||||
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "stream()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: operation bytes()]
|
||||
expected: FAIL
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue