diff options
author | Zhen Zhang <izgzhen@gmail.com> | 2016-05-17 12:52:35 +0800 |
---|---|---|
committer | Zhen Zhang <izgzhen@gmail.com> | 2016-06-01 09:47:07 +0800 |
commit | 43ad4ba5857dbcd1a31aaf7e441c5cfe4fe1b84f (patch) | |
tree | ef221985dfe1d3ed54e7a2af31903d8b1dbf40e4 /components/script/dom/blob.rs | |
parent | d53507f747f7122dc520f5e4a374ee1ad955aa5d (diff) | |
download | servo-43ad4ba5857dbcd1a31aaf7e441c5cfe4fe1b84f.tar.gz servo-43ad4ba5857dbcd1a31aaf7e441c5cfe4fe1b84f.zip |
Add file backend support for Blob and related
Changes include:
- Add BlobImpl to Blob, and related caching mechanism
- Expose ResourceThreads to document_loader, workerglobalscope, worker, and global
- Fix encode_multipart_form_data
- Other small fixes to accommodate the above changes
Diffstat (limited to 'components/script/dom/blob.rs')
-rw-r--r-- | components/script/dom/blob.rs | 134 |
1 files changed, 109 insertions, 25 deletions
diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs index 19fe0f4fdc2..b89aa3b9ac2 100644 --- a/components/script/dom/blob.rs +++ b/components/script/dom/blob.rs @@ -2,22 +2,26 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::BlobBinding; use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods; use dom::bindings::codegen::UnionTypes::BlobOrString; -use dom::bindings::error::Fallible; +use dom::bindings::error::{Error, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::js::Root; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use encoding::all::UTF_8; use encoding::types::{EncoderTrap, Encoding}; +use ipc_channel::ipc; +use net_traits::filemanager_thread::FileManagerThreadMsg; use num_traits::ToPrimitive; use std::ascii::AsciiExt; use std::borrow::ToOwned; use std::cell::Cell; use std::cmp::{max, min}; use std::sync::Arc; +use uuid::Uuid; #[derive(Clone, JSTraceable)] pub struct DataSlice { @@ -62,6 +66,11 @@ impl DataSlice { } } + /// Construct data slice from a vector of bytes + pub fn from_bytes(bytes: Vec<u8>) -> DataSlice { + DataSlice::new(Arc::new(bytes), None, None) + } + /// Construct an empty data slice pub fn empty() -> DataSlice { DataSlice { @@ -83,26 +92,51 @@ impl DataSlice { } +#[derive(Clone, JSTraceable)] +pub enum BlobImpl { + /// File-based, cached backend + File(Uuid, DOMRefCell<Option<DataSlice>>), + /// Memory-based backend + Memory(DataSlice), +} + +impl BlobImpl { + /// Construct memory-backed BlobImpl from DataSlice + pub fn new_from_slice(slice: DataSlice) -> BlobImpl { + BlobImpl::Memory(slice) + } + + /// Construct file-backed BlobImpl from File ID + pub fn new_from_file(file_id: Uuid) -> BlobImpl { + BlobImpl::File(file_id, DOMRefCell::new(None)) + } + + /// Construct empty, memory-backed BlobImpl + pub fn new_from_empty_slice() -> BlobImpl { + BlobImpl::new_from_slice(DataSlice::empty()) + } +} + // https://w3c.github.io/FileAPI/#blob #[dom_struct] pub struct Blob { reflector_: Reflector, #[ignore_heap_size_of = "No clear owner"] - data: DataSlice, + blob_impl: BlobImpl, typeString: String, isClosed_: Cell<bool>, } impl Blob { - pub fn new(global: GlobalRef, slice: DataSlice, typeString: &str) -> Root<Blob> { - let boxed_blob = box Blob::new_inherited(slice, typeString); + pub fn new(global: GlobalRef, blob_impl: BlobImpl, typeString: &str) -> Root<Blob> { + let boxed_blob = box Blob::new_inherited(blob_impl, typeString); reflect_dom_object(boxed_blob, global, BlobBinding::Wrap) } - pub fn new_inherited(slice: DataSlice, typeString: &str) -> Blob { + pub fn new_inherited(blob_impl: BlobImpl, typeString: &str) -> Blob { Blob { reflector_: Reflector::new(), - data: slice, + blob_impl: blob_impl, typeString: typeString.to_owned(), isClosed_: Cell::new(false), } @@ -116,35 +150,81 @@ impl Blob { // TODO: accept other blobParts types - ArrayBuffer or ArrayBufferView let bytes: Vec<u8> = match blobParts { None => Vec::new(), - Some(blobparts) => blob_parts_to_bytes(blobparts), + Some(blobparts) => match blob_parts_to_bytes(blobparts) { + Ok(bytes) => bytes, + Err(_) => return Err(Error::InvalidCharacter), + } }; - let slice = DataSlice::new(Arc::new(bytes), None, None); - Ok(Blob::new(global, slice, &blobPropertyBag.get_typestring())) + let slice = DataSlice::from_bytes(bytes); + Ok(Blob::new(global, BlobImpl::new_from_slice(slice), &blobPropertyBag.get_typestring())) } - pub fn get_data(&self) -> &DataSlice { - &self.data + /// Get a slice to inner data, this might incur synchronous read and caching + pub fn get_slice(&self) -> Result<DataSlice, ()> { + match self.blob_impl { + BlobImpl::File(ref id, ref slice) => { + match *slice.borrow() { + Some(ref s) => Ok(s.clone()), + None => { + let global = self.global(); + let s = read_file(global.r(), id.clone())?; + *slice.borrow_mut() = Some(s.clone()); // Cached + Ok(s) + } + } + } + BlobImpl::Memory(ref s) => Ok(s.clone()) + } + } + + /// Try to get a slice, and if any exception happens, return the empty slice + pub fn get_slice_or_empty(&self) -> DataSlice { + self.get_slice().unwrap_or(DataSlice::empty()) } } -pub fn blob_parts_to_bytes(blobparts: Vec<BlobOrString>) -> Vec<u8> { - blobparts.iter().flat_map(|blobpart| { - match blobpart { - &BlobOrString::String(ref s) => { - UTF_8.encode(s, EncoderTrap::Replace).unwrap() - }, - &BlobOrString::Blob(ref b) => { - b.get_data().get_bytes().to_vec() - }, - } - }).collect::<Vec<u8>>() +fn read_file(global: GlobalRef, id: Uuid) -> Result<DataSlice, ()> { + let file_manager = global.filemanager_thread(); + let (chan, recv) = ipc::channel().map_err(|_|())?; + let _ = file_manager.send(FileManagerThreadMsg::ReadFile(chan, id)); + + let result = match recv.recv() { + Ok(ret) => ret, + Err(e) => { + debug!("File manager thread has problem {:?}", e); + return Err(()) + } + }; + + let bytes = result.map_err(|_|())?; + Ok(DataSlice::from_bytes(bytes)) +} + +/// Extract bytes from BlobParts, used by Blob and File constructor +/// https://w3c.github.io/FileAPI/#constructorBlob +pub fn blob_parts_to_bytes(blobparts: Vec<BlobOrString>) -> Result<Vec<u8>, ()> { + let mut ret = vec![]; + + for blobpart in &blobparts { + match blobpart { + &BlobOrString::String(ref s) => { + let mut bytes = UTF_8.encode(s, EncoderTrap::Replace).map_err(|_|())?; + ret.append(&mut bytes); + }, + &BlobOrString::Blob(ref b) => { + ret.append(&mut b.get_slice_or_empty().bytes.to_vec()); + }, + } + } + + Ok(ret) } impl BlobMethods for Blob { // https://w3c.github.io/FileAPI/#dfn-size fn Size(&self) -> u64 { - self.data.size() + self.get_slice_or_empty().size() } // https://w3c.github.io/FileAPI/#dfn-type @@ -169,9 +249,11 @@ impl BlobMethods for Blob { } } }; + let global = self.global(); - let bytes = self.data.bytes.clone(); - Blob::new(global.r(), DataSlice::new(bytes, start, end), &relativeContentType) + let bytes = self.get_slice_or_empty().bytes.clone(); + let slice = DataSlice::new(bytes, start, end); + Blob::new(global.r(), BlobImpl::new_from_slice(slice), &relativeContentType) } // https://w3c.github.io/FileAPI/#dfn-isClosed @@ -196,6 +278,8 @@ impl BlobMethods for Blob { impl BlobBinding::BlobPropertyBag { + /// Get the normalized inner type string + /// https://w3c.github.io/FileAPI/#dfn-type pub fn get_typestring(&self) -> String { if is_ascii_printable(&self.type_) { self.type_.to_lowercase() |