diff options
author | Zhen Zhang <izgzhen@gmail.com> | 2016-06-18 18:14:40 +0800 |
---|---|---|
committer | Zhen Zhang <izgzhen@gmail.com> | 2016-07-04 23:02:03 +0800 |
commit | 14d68968edc936fe67a226840af4c10ff0aea350 (patch) | |
tree | 2d4e1fd6f7e1d4beeaca0f6697b26631d89e8e3b /components/script/dom/blob.rs | |
parent | 212aa4437e06af72ed3a215a1b49a4b1121f6398 (diff) | |
download | servo-14d68968edc936fe67a226840af4c10ff0aea350.tar.gz servo-14d68968edc936fe67a226840af4c10ff0aea350.zip |
Integration and improvements of File API backends
1. More complete origin check in FileManagerThreadMsg
2. Add reference counting logic to file manage store and script API
3. Integrate the support of slicing
Diffstat (limited to 'components/script/dom/blob.rs')
-rw-r--r-- | components/script/dom/blob.rs | 206 |
1 files changed, 158 insertions, 48 deletions
diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs index dcb4c68f34a..1bc0498fb35 100644 --- a/components/script/dom/blob.rs +++ b/components/script/dom/blob.rs @@ -8,17 +8,18 @@ use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods; use dom::bindings::codegen::UnionTypes::BlobOrString; use dom::bindings::error::{Error, Fallible}; use dom::bindings::global::GlobalRef; -use dom::bindings::js::Root; +use dom::bindings::js::{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, SelectedFileId}; -use num_traits::ToPrimitive; +use net_traits::IpcSend; +use net_traits::blob_url_store::BlobURLStoreEntry; +use net_traits::filemanager_thread::{FileManagerThreadMsg, SelectedFileId, RelativePos}; use std::ascii::AsciiExt; use std::cell::Cell; -use std::cmp::{max, min}; +use std::ops::Range; use std::sync::Arc; #[derive(Clone, JSTraceable)] @@ -31,36 +32,12 @@ pub struct DataSlice { impl DataSlice { /// Construct DataSlice from reference counted bytes pub fn new(bytes: Arc<Vec<u8>>, start: Option<i64>, end: Option<i64>) -> DataSlice { - let size = bytes.len() as i64; - let relativeStart: i64 = match start { - None => 0, - Some(start) => { - if start < 0 { - max(size + start, 0) - } else { - min(start, size) - } - } - }; - let relativeEnd: i64 = match end { - None => size, - Some(end) => { - if end < 0 { - max(size + end, 0) - } else { - min(end, size) - } - } - }; - - let span: i64 = max(relativeEnd - relativeStart, 0); - let start = relativeStart.to_usize().unwrap(); - let end = (relativeStart + span).to_usize().unwrap(); + let range = RelativePos::from_opts(start, end).to_abs_range(bytes.len()); DataSlice { bytes: bytes, - bytes_start: start, - bytes_end: end + bytes_start: range.start, + bytes_end: range.end, } } @@ -87,15 +64,30 @@ impl DataSlice { pub fn size(&self) -> u64 { (self.bytes_end as u64) - (self.bytes_start as u64) } -} + /// Further adjust the slice range based on passed-in relative positions + pub fn slice(&self, pos: &RelativePos) -> DataSlice { + let old_size = self.size(); + let range = pos.to_abs_range(old_size as usize); + DataSlice { + bytes: self.bytes.clone(), + bytes_start: self.bytes_start + range.start, + bytes_end: self.bytes_start + range.end, + } + } +} -#[derive(Clone, JSTraceable)] +#[must_root] +#[derive(JSTraceable)] pub enum BlobImpl { - /// File-based, cached backend + /// File-based blob, including id and possibly cached content File(SelectedFileId, DOMRefCell<Option<DataSlice>>), - /// Memory-based backend + /// Memory-based blob Memory(DataSlice), + /// Sliced blob, including parent blob and + /// relative positions representing current slicing range, + /// it is leaf of a two-layer fat tree + Sliced(JS<Blob>, RelativePos), } impl BlobImpl { @@ -120,26 +112,58 @@ impl BlobImpl { pub struct Blob { reflector_: Reflector, #[ignore_heap_size_of = "No clear owner"] - blob_impl: BlobImpl, + blob_impl: DOMRefCell<BlobImpl>, typeString: String, isClosed_: Cell<bool>, } impl Blob { + #[allow(unrooted_must_root)] pub fn new(global: GlobalRef, blob_impl: BlobImpl, typeString: String) -> Root<Blob> { let boxed_blob = box Blob::new_inherited(blob_impl, typeString); reflect_dom_object(boxed_blob, global, BlobBinding::Wrap) } + #[allow(unrooted_must_root)] pub fn new_inherited(blob_impl: BlobImpl, typeString: String) -> Blob { Blob { reflector_: Reflector::new(), - blob_impl: blob_impl, + blob_impl: DOMRefCell::new(blob_impl), typeString: typeString, isClosed_: Cell::new(false), } } + #[allow(unrooted_must_root)] + fn new_sliced(parent: &Blob, rel_pos: RelativePos, + relativeContentType: DOMString) -> Root<Blob> { + let global = parent.global(); + let blob_impl = match *parent.blob_impl.borrow() { + BlobImpl::File(ref id, _) => { + inc_ref_id(global.r(), id.clone()); + + // Create new parent node + BlobImpl::Sliced(JS::from_ref(parent), rel_pos) + } + BlobImpl::Memory(_) => { + // Create new parent node + BlobImpl::Sliced(JS::from_ref(parent), rel_pos) + } + BlobImpl::Sliced(ref grandparent, ref old_rel_pos) => { + // Adjust the slicing position, using same parent + let new_rel_pos = old_rel_pos.slice_inner(&rel_pos); + + if let BlobImpl::File(ref id, _) = *grandparent.blob_impl.borrow() { + inc_ref_id(global.r(), id.clone()); + } + + BlobImpl::Sliced(grandparent.clone(), new_rel_pos) + } + }; + + Blob::new(global.r(), blob_impl, relativeContentType.into()) + } + // https://w3c.github.io/FileAPI/#constructorBlob pub fn Constructor(global: GlobalRef, blobParts: Option<Vec<BlobOrString>>, @@ -160,19 +184,29 @@ impl Blob { /// 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() { + match *self.blob_impl.borrow() { + BlobImpl::File(ref id, ref cached) => { + let buffer = match *cached.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) } + }; + + // Cache + if let Ok(buf) = buffer.clone() { + *cached.borrow_mut() = Some(buf); } + + buffer + } + BlobImpl::Memory(ref s) => Ok(s.clone()), + BlobImpl::Sliced(ref parent, ref rel_pos) => { + let dataslice = parent.get_slice_or_empty(); + Ok(dataslice.slice(rel_pos)) } - BlobImpl::Memory(ref s) => Ok(s.clone()) } } @@ -180,12 +214,83 @@ impl Blob { pub fn get_slice_or_empty(&self) -> DataSlice { self.get_slice().unwrap_or(DataSlice::empty()) } + + pub fn get_id(&self) -> SelectedFileId { + match *self.blob_impl.borrow() { + BlobImpl::File(ref id, _) => id.clone(), + BlobImpl::Memory(ref slice) => self.promote_to_file(slice), + BlobImpl::Sliced(ref parent, ref rel_pos) => { + match *parent.blob_impl.borrow() { + BlobImpl::Sliced(_, _) => { + debug!("Sliced can't have a sliced parent"); + // Return dummy id + SelectedFileId("".to_string()) + } + BlobImpl::File(ref parent_id, _) => + self.create_sliced_id(parent_id, rel_pos), + BlobImpl::Memory(ref parent_slice) => { + let parent_id = parent.promote_to_file(parent_slice); + *self.blob_impl.borrow_mut() = BlobImpl::Sliced(parent.clone(), rel_pos.clone()); + self.create_sliced_id(&parent_id, rel_pos) + } + } + } + } + } + + /// Promite memory-based Blob to file-based, + /// The bytes in data slice will be transferred to file manager thread + fn promote_to_file(&self, self_slice: &DataSlice) -> SelectedFileId { + let global = self.global(); + let origin = global.r().get_url().origin().unicode_serialization(); + let filemanager = global.r().resource_threads().sender(); + let bytes = self_slice.get_bytes(); + let rel_pos = RelativePos::from_abs_range(Range { + start: self_slice.bytes_start, + end: self_slice.bytes_end, + }, self_slice.bytes.len()); + + let entry = BlobURLStoreEntry { + type_string: self.typeString.clone(), + size: self.Size(), + bytes: bytes.to_vec(), + }; + + let (tx, rx) = ipc::channel().unwrap(); + let _ = filemanager.send(FileManagerThreadMsg::TransferMemory(entry, rel_pos, tx, origin.clone())); + + match rx.recv().unwrap() { + Ok(new_id) => SelectedFileId(new_id.0), + // Dummy id + Err(_) => SelectedFileId("".to_string()), + } + } + + fn create_sliced_id(&self, parent_id: &SelectedFileId, + rel_pos: &RelativePos) -> SelectedFileId { + let global = self.global(); + + let origin = global.r().get_url().origin().unicode_serialization(); + + let filemanager = global.r().resource_threads().sender(); + let (tx, rx) = ipc::channel().unwrap(); + let msg = FileManagerThreadMsg::AddSlicedEntry(parent_id.clone(), + rel_pos.clone(), + tx, origin.clone()); + let _ = filemanager.send(msg); + let new_id = rx.recv().unwrap().unwrap(); + + // Return the indirect id reference + SelectedFileId(new_id.0) + } } fn read_file(global: GlobalRef, id: SelectedFileId) -> Result<DataSlice, ()> { let file_manager = global.filemanager_thread(); let (chan, recv) = ipc::channel().map_err(|_|())?; - let _ = file_manager.send(FileManagerThreadMsg::ReadFile(chan, id)); + let origin = global.get_url().origin().unicode_serialization(); + let msg = FileManagerThreadMsg::ReadFile(chan, id, origin); + let _ = file_manager.send(msg); let result = match recv.recv() { Ok(ret) => ret, @@ -248,10 +353,8 @@ impl BlobMethods for Blob { } }; - let global = self.global(); - 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.into()) + let rel_pos = RelativePos::from_opts(start, end); + Blob::new_sliced(self, rel_pos, relativeContentType) } // https://w3c.github.io/FileAPI/#dfn-isClosed @@ -274,7 +377,6 @@ impl BlobMethods for Blob { } } - impl BlobBinding::BlobPropertyBag { /// Get the normalized inner type string /// https://w3c.github.io/FileAPI/#dfn-type @@ -292,3 +394,11 @@ fn is_ascii_printable(string: &str) -> bool { // https://w3c.github.io/FileAPI/#constructorBlob string.chars().all(|c| c >= '\x20' && c <= '\x7E') } + +/// Bump the reference counter in file manager thread +fn inc_ref_id(global: GlobalRef, id: SelectedFileId) { + let file_manager = global.filemanager_thread(); + let origin = global.get_url().origin().unicode_serialization(); + let msg = FileManagerThreadMsg::IncRef(id, origin); + let _ = file_manager.send(msg); +} |