mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Implement Blob methods (text/arraybuffer) and async file read method
This commit is contained in:
parent
1b7223a284
commit
9859410193
8 changed files with 188 additions and 115 deletions
|
@ -202,7 +202,7 @@ fn run_form_data_algorithm(
|
|||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
fn run_array_buffer_data_algorithm(cx: JSContext, bytes: Vec<u8>) -> Fallible<FetchedData> {
|
||||
pub fn run_array_buffer_data_algorithm(cx: JSContext, bytes: Vec<u8>) -> Fallible<FetchedData> {
|
||||
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
|
||||
let arraybuffer = unsafe {
|
||||
ArrayBuffer::create(
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* 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 crate::body::{run_array_buffer_data_algorithm, FetchedData};
|
||||
use crate::dom::bindings::codegen::Bindings::BlobBinding;
|
||||
use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
|
||||
use crate::dom::bindings::codegen::UnionTypes::ArrayBufferOrArrayBufferViewOrBlobOrString;
|
||||
|
@ -12,12 +13,16 @@ use crate::dom::bindings::serializable::{Serializable, StorageKey};
|
|||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::bindings::structuredclone::StructuredDataHolder;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::promise::Promise;
|
||||
use crate::realms::{AlreadyInRealm, InRealm};
|
||||
use dom_struct::dom_struct;
|
||||
use encoding_rs::UTF_8;
|
||||
use msg::constellation_msg::{BlobId, BlobIndex, PipelineNamespaceId};
|
||||
use net_traits::filemanager_thread::RelativePos;
|
||||
use script_traits::serializable::BlobImpl;
|
||||
use std::collections::HashMap;
|
||||
use std::num::NonZeroU32;
|
||||
use std::rc::Rc;
|
||||
use uuid::Uuid;
|
||||
|
||||
// https://w3c.github.io/FileAPI/#blob
|
||||
|
@ -223,6 +228,59 @@ impl BlobMethods for Blob {
|
|||
let blob_impl = BlobImpl::new_sliced(rel_pos, self.blob_id.clone(), type_string);
|
||||
Blob::new(&*self.global(), blob_impl)
|
||||
}
|
||||
|
||||
// https://w3c.github.io/FileAPI/#text-method-algo
|
||||
fn Text(&self) -> Rc<Promise> {
|
||||
let global = self.global();
|
||||
let in_realm_proof = AlreadyInRealm::assert(&global);
|
||||
let p = Promise::new_in_current_realm(&global, InRealm::Already(&in_realm_proof));
|
||||
let id = self.get_blob_url_id();
|
||||
global.read_file_async(
|
||||
id,
|
||||
p.clone(),
|
||||
Box::new(|promise, bytes| match bytes {
|
||||
Ok(b) => {
|
||||
let (text, _, _) = UTF_8.decode(&b);
|
||||
let text = DOMString::from(text);
|
||||
promise.resolve_native(&text);
|
||||
},
|
||||
Err(e) => {
|
||||
promise.reject_error(e);
|
||||
},
|
||||
}),
|
||||
);
|
||||
p
|
||||
}
|
||||
|
||||
// https://w3c.github.io/FileAPI/#arraybuffer-method-algo
|
||||
fn ArrayBuffer(&self) -> Rc<Promise> {
|
||||
let global = self.global();
|
||||
let in_realm_proof = AlreadyInRealm::assert(&global);
|
||||
let p = Promise::new_in_current_realm(&global, InRealm::Already(&in_realm_proof));
|
||||
|
||||
let id = self.get_blob_url_id();
|
||||
|
||||
global.read_file_async(
|
||||
id,
|
||||
p.clone(),
|
||||
Box::new(|promise, bytes| {
|
||||
match bytes {
|
||||
Ok(b) => {
|
||||
let cx = promise.global().get_cx();
|
||||
let result = run_array_buffer_data_algorithm(cx, b);
|
||||
|
||||
match result {
|
||||
Ok(FetchedData::ArrayBuffer(a)) => promise.resolve_native(&a),
|
||||
Err(e) => promise.reject_error(e),
|
||||
_ => panic!("Unexpected result from run_array_buffer_data_algorithm"),
|
||||
}
|
||||
},
|
||||
Err(e) => promise.reject_error(e),
|
||||
};
|
||||
}),
|
||||
);
|
||||
p
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the normalized, MIME-parsable type string
|
||||
|
|
|
@ -8,9 +8,9 @@ use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction;
|
|||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
|
||||
use crate::dom::bindings::conversions::{root_from_object, root_from_object_static};
|
||||
use crate::dom::bindings::error::{report_pending_exception, ErrorInfo};
|
||||
use crate::dom::bindings::error::{report_pending_exception, Error, ErrorInfo};
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::refcounted::Trusted;
|
||||
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
|
||||
use crate::dom::bindings::reflector::DomObject;
|
||||
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
|
||||
use crate::dom::bindings::settings_stack::{entry_global, incumbent_global, AutoEntryScript};
|
||||
|
@ -31,10 +31,12 @@ use crate::dom::messageevent::MessageEvent;
|
|||
use crate::dom::messageport::MessagePort;
|
||||
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
|
||||
use crate::dom::performance::Performance;
|
||||
use crate::dom::promise::Promise;
|
||||
use crate::dom::window::Window;
|
||||
use crate::dom::workerglobalscope::WorkerGlobalScope;
|
||||
use crate::dom::workletglobalscope::WorkletGlobalScope;
|
||||
use crate::microtask::{Microtask, MicrotaskQueue, UserMicrotask};
|
||||
use crate::realms::enter_realm;
|
||||
use crate::script_module::ModuleTree;
|
||||
use crate::script_runtime::{CommonScriptMsg, JSContext as SafeJSContext, ScriptChan, ScriptPort};
|
||||
use crate::script_thread::{MainThreadScriptChan, ScriptThread};
|
||||
|
@ -69,7 +71,9 @@ use js::rust::{HandleValue, MutableHandleValue};
|
|||
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
|
||||
use msg::constellation_msg::{BlobId, MessagePortId, MessagePortRouterId, PipelineId};
|
||||
use net_traits::blob_url_store::{get_blob_origin, BlobBuf};
|
||||
use net_traits::filemanager_thread::{FileManagerThreadMsg, ReadFileProgress, RelativePos};
|
||||
use net_traits::filemanager_thread::{
|
||||
FileManagerResult, FileManagerThreadMsg, ReadFileProgress, RelativePos,
|
||||
};
|
||||
use net_traits::image_cache::ImageCache;
|
||||
use net_traits::{CoreResourceMsg, CoreResourceThread, IpcSend, ResourceThreads};
|
||||
use profile_traits::{ipc as profile_ipc, mem as profile_mem, time as profile_time};
|
||||
|
@ -231,6 +235,23 @@ struct TimerListener {
|
|||
context: Trusted<GlobalScope>,
|
||||
}
|
||||
|
||||
/// A wrapper for the handling of file data received by the ipc router
|
||||
struct FileListener {
|
||||
/// State should progress as either of:
|
||||
/// - Some(Empty) => Some(Receiving) => None
|
||||
/// - Some(Empty) => None
|
||||
state: Option<FileListenerState>,
|
||||
task_source: FileReadingTaskSource,
|
||||
task_canceller: TaskCanceller,
|
||||
}
|
||||
|
||||
struct FileListenerCallback(Box<dyn Fn(Rc<Promise>, Result<Vec<u8>, Error>) + Send>);
|
||||
|
||||
enum FileListenerState {
|
||||
Empty(FileListenerCallback, TrustedPromise),
|
||||
Receiving(Vec<u8>, FileListenerCallback, TrustedPromise),
|
||||
}
|
||||
|
||||
#[derive(JSTraceable, MallocSizeOf)]
|
||||
/// A holder of a weak reference for a DOM blob or file.
|
||||
pub enum BlobTracker {
|
||||
|
@ -389,6 +410,64 @@ impl MessageListener {
|
|||
}
|
||||
}
|
||||
|
||||
impl FileListener {
|
||||
fn handle(&mut self, msg: FileManagerResult<ReadFileProgress>) {
|
||||
match msg {
|
||||
Ok(ReadFileProgress::Meta(blob_buf)) => match self.state.take() {
|
||||
Some(FileListenerState::Empty(callback, promise)) => {
|
||||
self.state = Some(FileListenerState::Receiving(
|
||||
blob_buf.bytes,
|
||||
callback,
|
||||
promise,
|
||||
));
|
||||
},
|
||||
_ => panic!(
|
||||
"Unexpected FileListenerState when receiving ReadFileProgress::Meta msg."
|
||||
),
|
||||
},
|
||||
Ok(ReadFileProgress::Partial(mut bytes_in)) => match self.state.take() {
|
||||
Some(FileListenerState::Receiving(mut bytes, callback, promise)) => {
|
||||
bytes.append(&mut bytes_in);
|
||||
self.state = Some(FileListenerState::Receiving(bytes, callback, promise));
|
||||
},
|
||||
_ => panic!(
|
||||
"Unexpected FileListenerState when receiving ReadFileProgress::Partial msg."
|
||||
),
|
||||
},
|
||||
Ok(ReadFileProgress::EOF) => match self.state.take() {
|
||||
Some(FileListenerState::Receiving(bytes, callback, trusted_promise)) => {
|
||||
let _ = self.task_source.queue_with_canceller(
|
||||
task!(resolve_promise: move || {
|
||||
let promise = trusted_promise.root();
|
||||
let _ac = enter_realm(&*promise.global());
|
||||
callback.0(promise, Ok(bytes));
|
||||
}),
|
||||
&self.task_canceller,
|
||||
);
|
||||
},
|
||||
_ => {
|
||||
panic!("Unexpected FileListenerState when receiving ReadFileProgress::EOF msg.")
|
||||
},
|
||||
},
|
||||
Err(_) => match self.state.take() {
|
||||
Some(FileListenerState::Receiving(_, callback, trusted_promise)) |
|
||||
Some(FileListenerState::Empty(callback, trusted_promise)) => {
|
||||
let bytes = Err(Error::Network);
|
||||
let _ = self.task_source.queue_with_canceller(
|
||||
task!(reject_promise: move || {
|
||||
let promise = trusted_promise.root();
|
||||
let _ac = enter_realm(&*promise.global());
|
||||
callback.0(promise, bytes);
|
||||
}),
|
||||
&self.task_canceller,
|
||||
);
|
||||
},
|
||||
_ => panic!("Unexpected FileListenerState when receiving Err msg."),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalScope {
|
||||
pub fn new_inherited(
|
||||
pipeline_id: PipelineId,
|
||||
|
@ -1251,18 +1330,59 @@ impl GlobalScope {
|
|||
}
|
||||
|
||||
fn read_file(&self, id: Uuid) -> Result<Vec<u8>, ()> {
|
||||
let recv = self.send_msg(id);
|
||||
GlobalScope::read_msg(recv)
|
||||
}
|
||||
|
||||
pub fn read_file_async(
|
||||
&self,
|
||||
id: Uuid,
|
||||
promise: Rc<Promise>,
|
||||
callback: Box<dyn Fn(Rc<Promise>, Result<Vec<u8>, Error>) + Send>,
|
||||
) {
|
||||
let recv = self.send_msg(id);
|
||||
|
||||
let trusted_promise = TrustedPromise::new(promise);
|
||||
let task_canceller = self.task_canceller(TaskSourceName::FileReading);
|
||||
let task_source = self.file_reading_task_source();
|
||||
|
||||
let mut file_listener = FileListener {
|
||||
state: Some(FileListenerState::Empty(
|
||||
FileListenerCallback(callback),
|
||||
trusted_promise,
|
||||
)),
|
||||
task_source,
|
||||
task_canceller,
|
||||
};
|
||||
|
||||
ROUTER.add_route(
|
||||
recv.to_opaque(),
|
||||
Box::new(move |msg| {
|
||||
file_listener.handle(
|
||||
msg.to()
|
||||
.expect("Deserialization of file listener msg failed."),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
fn send_msg(&self, id: Uuid) -> profile_ipc::IpcReceiver<FileManagerResult<ReadFileProgress>> {
|
||||
let resource_threads = self.resource_threads();
|
||||
let (chan, recv) =
|
||||
profile_ipc::channel(self.time_profiler_chan().clone()).map_err(|_| ())?;
|
||||
let (chan, recv) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap();
|
||||
let origin = get_blob_origin(&self.get_url());
|
||||
let check_url_validity = false;
|
||||
let msg = FileManagerThreadMsg::ReadFile(chan, id, check_url_validity, origin);
|
||||
let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
|
||||
recv
|
||||
}
|
||||
|
||||
fn read_msg(
|
||||
receiver: profile_ipc::IpcReceiver<FileManagerResult<ReadFileProgress>>,
|
||||
) -> Result<Vec<u8>, ()> {
|
||||
let mut bytes = vec![];
|
||||
|
||||
loop {
|
||||
match recv.recv().unwrap() {
|
||||
match receiver.recv().unwrap() {
|
||||
Ok(ReadFileProgress::Meta(mut blob_buf)) => {
|
||||
bytes.append(&mut blob_buf.bytes);
|
||||
},
|
||||
|
|
|
@ -16,6 +16,9 @@ interface Blob {
|
|||
Blob slice(optional [Clamp] long long start,
|
||||
optional [Clamp] long long end,
|
||||
optional DOMString contentType);
|
||||
|
||||
[NewObject] Promise<DOMString> text();
|
||||
[NewObject] Promise<ArrayBuffer> arrayBuffer();
|
||||
};
|
||||
|
||||
dictionary BlobPropertyBag {
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
[Blob-array-buffer.any.html]
|
||||
[Blob.arrayBuffer() non-unicode input]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.arrayBuffer()]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.arrayBuffer() non-ascii input]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.arrayBuffer() empty Blob data]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.arrayBuffer() concurrent reads]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[Blob-array-buffer.any.worker.html]
|
||||
[Blob.arrayBuffer() non-unicode input]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.arrayBuffer()]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.arrayBuffer() non-ascii input]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.arrayBuffer() empty Blob data]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.arrayBuffer() concurrent reads]
|
||||
expected: FAIL
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
[Blob-text.any.html]
|
||||
[Blob.text() different charset param in type option]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text() different charset param with non-ascii input]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text() multi-element array in constructor]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text() invalid utf-8 input]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text() non-unicode]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text()]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text() empty blob data]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text() concurrent reads]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[Blob-text.any.worker.html]
|
||||
[Blob.text() different charset param in type option]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text() different charset param with non-ascii input]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text() multi-element array in constructor]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text() invalid utf-8 input]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text() non-unicode]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text()]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text() empty blob data]
|
||||
expected: FAIL
|
||||
|
||||
[Blob.text() concurrent reads]
|
||||
expected: FAIL
|
||||
|
|
@ -18,21 +18,12 @@
|
|||
[File API automated IDL tests]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "text()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: operation stream()]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new Blob(["TEST"\]) must inherit property "arrayBuffer()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: operation text()]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new Blob(["TEST"\]) must inherit property "text()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new Blob(["TEST"\]) must inherit property "stream()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -42,6 +33,3 @@
|
|||
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "stream()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "arrayBuffer()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -30,21 +30,12 @@
|
|||
[idlharness]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "text()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: operation stream()]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new Blob(["TEST"\]) must inherit property "arrayBuffer()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: operation text()]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new Blob(["TEST"\]) must inherit property "text()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new Blob(["TEST"\]) must inherit property "stream()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -54,6 +45,3 @@
|
|||
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "stream()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Blob interface: new File(["myFileBits"\], "myFileName") must inherit property "arrayBuffer()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue