diff options
author | Kunal Mohan <kunalmohan99@gmail.com> | 2020-01-12 23:27:59 +0530 |
---|---|---|
committer | Kunal Mohan <kunalmohan99@gmail.com> | 2020-01-28 14:38:32 +0530 |
commit | 9859410193b07f6bfddcb991765abe3fb47eef0c (patch) | |
tree | 704d17f5b61594b384d70044e59683ffe0e111e5 | |
parent | 1b7223a284cd8f780d2856a50747ffc97beffd23 (diff) | |
download | servo-9859410193b07f6bfddcb991765abe3fb47eef0c.tar.gz servo-9859410193b07f6bfddcb991765abe3fb47eef0c.zip |
Implement Blob methods (text/arraybuffer) and async file read method
-rw-r--r-- | components/script/body.rs | 2 | ||||
-rw-r--r-- | components/script/dom/blob.rs | 58 | ||||
-rw-r--r-- | components/script/dom/globalscope.rs | 132 | ||||
-rw-r--r-- | components/script/dom/webidls/Blob.webidl | 3 | ||||
-rw-r--r-- | tests/wpt/metadata/FileAPI/blob/Blob-array-buffer.any.js.ini | 33 | ||||
-rw-r--r-- | tests/wpt/metadata/FileAPI/blob/Blob-text.any.js.ini | 51 | ||||
-rw-r--r-- | tests/wpt/metadata/FileAPI/idlharness.html.ini | 12 | ||||
-rw-r--r-- | tests/wpt/metadata/FileAPI/idlharness.worker.js.ini | 12 |
8 files changed, 188 insertions, 115 deletions
diff --git a/components/script/body.rs b/components/script/body.rs index d5a63d7bcf0..e2d9fb925fb 100644 --- a/components/script/body.rs +++ b/components/script/body.rs @@ -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( diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs index f8b29b895fd..bc8799a3d8d 100644 --- a/components/script/dom/blob.rs +++ b/components/script/dom/blob.rs @@ -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 diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 77cded77230..f8427ffd083 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -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); }, diff --git a/components/script/dom/webidls/Blob.webidl b/components/script/dom/webidls/Blob.webidl index 15d28cadb99..8a3f5f5185e 100644 --- a/components/script/dom/webidls/Blob.webidl +++ b/components/script/dom/webidls/Blob.webidl @@ -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 { diff --git a/tests/wpt/metadata/FileAPI/blob/Blob-array-buffer.any.js.ini b/tests/wpt/metadata/FileAPI/blob/Blob-array-buffer.any.js.ini deleted file mode 100644 index a9548bac2f9..00000000000 --- a/tests/wpt/metadata/FileAPI/blob/Blob-array-buffer.any.js.ini +++ /dev/null @@ -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 - diff --git a/tests/wpt/metadata/FileAPI/blob/Blob-text.any.js.ini b/tests/wpt/metadata/FileAPI/blob/Blob-text.any.js.ini deleted file mode 100644 index aa09d7ecbe6..00000000000 --- a/tests/wpt/metadata/FileAPI/blob/Blob-text.any.js.ini +++ /dev/null @@ -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 - diff --git a/tests/wpt/metadata/FileAPI/idlharness.html.ini b/tests/wpt/metadata/FileAPI/idlharness.html.ini index 3c3fa133edc..a19d956ea41 100644 --- a/tests/wpt/metadata/FileAPI/idlharness.html.ini +++ b/tests/wpt/metadata/FileAPI/idlharness.html.ini @@ -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 - diff --git a/tests/wpt/metadata/FileAPI/idlharness.worker.js.ini b/tests/wpt/metadata/FileAPI/idlharness.worker.js.ini index 7a8f638a336..b8d0a66f512 100644 --- a/tests/wpt/metadata/FileAPI/idlharness.worker.js.ini +++ b/tests/wpt/metadata/FileAPI/idlharness.worker.js.ini @@ -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 - |