mirror of
https://github.com/servo/servo.git
synced 2025-07-22 14:53:49 +01:00
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
This commit is contained in:
parent
212aa4437e
commit
14d68968ed
14 changed files with 543 additions and 244 deletions
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue