aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKunal Mohan <kunalmohan99@gmail.com>2020-01-12 23:27:59 +0530
committerKunal Mohan <kunalmohan99@gmail.com>2020-01-28 14:38:32 +0530
commit9859410193b07f6bfddcb991765abe3fb47eef0c (patch)
tree704d17f5b61594b384d70044e59683ffe0e111e5
parent1b7223a284cd8f780d2856a50747ffc97beffd23 (diff)
downloadservo-9859410193b07f6bfddcb991765abe3fb47eef0c.tar.gz
servo-9859410193b07f6bfddcb991765abe3fb47eef0c.zip
Implement Blob methods (text/arraybuffer) and async file read method
-rw-r--r--components/script/body.rs2
-rw-r--r--components/script/dom/blob.rs58
-rw-r--r--components/script/dom/globalscope.rs132
-rw-r--r--components/script/dom/webidls/Blob.webidl3
-rw-r--r--tests/wpt/metadata/FileAPI/blob/Blob-array-buffer.any.js.ini33
-rw-r--r--tests/wpt/metadata/FileAPI/blob/Blob-text.any.js.ini51
-rw-r--r--tests/wpt/metadata/FileAPI/idlharness.html.ini12
-rw-r--r--tests/wpt/metadata/FileAPI/idlharness.worker.js.ini12
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
-