mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01: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::collections::HashMap;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
use std::ptr;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use base::id::{BlobId, BlobIndex, PipelineNamespaceId};
|
use base::id::{BlobId, BlobIndex, PipelineNamespaceId};
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use encoding_rs::UTF_8;
|
use encoding_rs::UTF_8;
|
||||||
|
use js::jsapi::JSObject;
|
||||||
use js::rust::HandleObject;
|
use js::rust::HandleObject;
|
||||||
|
use js::typedarray::Uint8;
|
||||||
use net_traits::filemanager_thread::RelativePos;
|
use net_traits::filemanager_thread::RelativePos;
|
||||||
use script_traits::serializable::BlobImpl;
|
use script_traits::serializable::BlobImpl;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::body::{run_array_buffer_data_algorithm, FetchedData};
|
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;
|
||||||
use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
|
use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
|
||||||
use crate::dom::bindings::codegen::UnionTypes::ArrayBufferOrArrayBufferViewOrBlobOrString;
|
use crate::dom::bindings::codegen::UnionTypes::ArrayBufferOrArrayBufferViewOrBlobOrString;
|
||||||
|
@ -297,6 +301,46 @@ impl BlobMethods<crate::DomTypeHolder> for Blob {
|
||||||
);
|
);
|
||||||
p
|
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
|
/// Get the normalized, MIME-parsable type string
|
||||||
|
|
|
@ -28,11 +28,137 @@ use crate::dom::defaultteereadrequest::DefaultTeeReadRequest;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::promise::Promise;
|
use crate::dom::promise::Promise;
|
||||||
use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
|
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::dom::readablestreamgenericreader::ReadableStreamGenericReader;
|
||||||
use crate::realms::{enter_realm, InRealm};
|
use crate::realms::{enter_realm, InRealm};
|
||||||
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
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>
|
/// <https://streams.spec.whatwg.org/#read-request>
|
||||||
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||||
pub(crate) enum ReadRequest {
|
pub(crate) enum ReadRequest {
|
||||||
|
@ -348,6 +474,37 @@ impl ReadableStreamDefaultReader {
|
||||||
.borrow()
|
.borrow()
|
||||||
.append_native_handler(&handler, comp, can_gc);
|
.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 {
|
impl ReadableStreamDefaultReaderMethods<crate::DomTypeHolder> for ReadableStreamDefaultReader {
|
||||||
|
|
|
@ -34,7 +34,8 @@ DOMInterfaces = {
|
||||||
|
|
||||||
'Blob': {
|
'Blob': {
|
||||||
'weakReferenceable': True,
|
'weakReferenceable': True,
|
||||||
'canGc': ['Slice', 'Text', 'ArrayBuffer', 'Stream'],
|
'canGc': ['Slice', 'Text', 'ArrayBuffer', 'Stream', 'Bytes'],
|
||||||
|
'inRealms': ['Bytes'],
|
||||||
},
|
},
|
||||||
|
|
||||||
'Bluetooth': {
|
'Bluetooth': {
|
||||||
|
|
|
@ -20,6 +20,7 @@ interface Blob {
|
||||||
[NewObject, Throws] ReadableStream stream();
|
[NewObject, Throws] ReadableStream stream();
|
||||||
[NewObject] Promise<DOMString> text();
|
[NewObject] Promise<DOMString> text();
|
||||||
[NewObject] Promise<ArrayBuffer> arrayBuffer();
|
[NewObject] Promise<ArrayBuffer> arrayBuffer();
|
||||||
|
[NewObject] Promise<Uint8Array> bytes();
|
||||||
};
|
};
|
||||||
|
|
||||||
dictionary BlobPropertyBag {
|
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]
|
[idlharness.any.html]
|
||||||
[Blob interface: operation text()]
|
|
||||||
|
|
||||||
[Blob interface: operation arrayBuffer()]
|
|
||||||
|
|
||||||
[FileReader interface: operation readAsBinaryString(Blob)]
|
[FileReader interface: operation readAsBinaryString(Blob)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -12,21 +8,8 @@
|
||||||
[FileReader interface: calling readAsBinaryString(Blob) on new FileReader() with too few arguments must throw TypeError]
|
[FileReader interface: calling readAsBinaryString(Blob) on new FileReader() with too few arguments must throw TypeError]
|
||||||
expected: FAIL
|
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]
|
[idlharness.any.worker.html]
|
||||||
[Blob interface: operation text()]
|
|
||||||
|
|
||||||
[Blob interface: operation arrayBuffer()]
|
|
||||||
|
|
||||||
[FileReader interface: operation readAsBinaryString(Blob)]
|
[FileReader interface: operation readAsBinaryString(Blob)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -35,12 +18,3 @@
|
||||||
|
|
||||||
[FileReader interface: calling readAsBinaryString(Blob) on new FileReader() with too few arguments must throw TypeError]
|
[FileReader interface: calling readAsBinaryString(Blob) on new FileReader() with too few arguments must throw TypeError]
|
||||||
expected: FAIL
|
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]
|
[idlharness.html]
|
||||||
[Blob interface: operation text()]
|
|
||||||
|
|
||||||
[Blob interface: operation arrayBuffer()]
|
|
||||||
|
|
||||||
[FileReader interface: operation readAsBinaryString(Blob)]
|
[FileReader interface: operation readAsBinaryString(Blob)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -17,6 +13,3 @@
|
||||||
|
|
||||||
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "stream()" with the proper type]
|
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "stream()" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Blob interface: operation bytes()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
[idlharness.worker.html]
|
[idlharness.worker.html]
|
||||||
[Blob interface: operation text()]
|
|
||||||
|
|
||||||
[Blob interface: operation arrayBuffer()]
|
|
||||||
|
|
||||||
[FileReader interface: operation readAsBinaryString(Blob)]
|
[FileReader interface: operation readAsBinaryString(Blob)]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -17,6 +13,3 @@
|
||||||
|
|
||||||
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "stream()" with the proper type]
|
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "stream()" with the proper type]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Blob interface: operation bytes()]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue