diff options
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); +} |