mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Auto merge of #11875 - izgzhen:file-manager-backend, r=Manishearth
Integration and improvements of File API backends Basically three major changes: 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 r? @Manishearth --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/11875) <!-- Reviewable:end -->
This commit is contained in:
commit
36974f0746
14 changed files with 543 additions and 244 deletions
|
@ -57,7 +57,7 @@ use js::jsval::JSVal;
|
|||
use js::rust::Runtime;
|
||||
use libc;
|
||||
use msg::constellation_msg::{FrameType, PipelineId, SubpageId, WindowSizeData, WindowSizeType, ReferrerPolicy};
|
||||
use net_traits::filemanager_thread::SelectedFileId;
|
||||
use net_traits::filemanager_thread::{SelectedFileId, RelativePos};
|
||||
use net_traits::image::base::{Image, ImageMetadata};
|
||||
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
|
||||
use net_traits::response::HttpsState;
|
||||
|
@ -331,6 +331,7 @@ no_jsmanaged_fields!(ReferrerPolicy);
|
|||
no_jsmanaged_fields!(ResourceThreads);
|
||||
no_jsmanaged_fields!(SystemTime);
|
||||
no_jsmanaged_fields!(SelectedFileId);
|
||||
no_jsmanaged_fields!(RelativePos);
|
||||
no_jsmanaged_fields!(OpaqueStyleAndLayoutData);
|
||||
no_jsmanaged_fields!(CSSErrorReporter);
|
||||
no_jsmanaged_fields!(WebGLBufferId);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ pub struct File {
|
|||
}
|
||||
|
||||
impl File {
|
||||
#[allow(unrooted_must_root)]
|
||||
fn new_inherited(blob_impl: BlobImpl, name: DOMString,
|
||||
modified: Option<i64>, typeString: &str) -> File {
|
||||
File {
|
||||
|
@ -39,6 +40,7 @@ impl File {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn new(global: GlobalRef, blob_impl: BlobImpl,
|
||||
name: DOMString, modified: Option<i64>, typeString: &str) -> Root<File> {
|
||||
reflect_dom_object(box File::new_inherited(blob_impl, name, modified, typeString),
|
||||
|
|
|
@ -1152,6 +1152,7 @@ impl Activatable for HTMLInputElement {
|
|||
InputType::InputFile => {
|
||||
// https://html.spec.whatwg.org/multipage/#file-upload-state-(type=file)
|
||||
let window = window_from_node(self);
|
||||
let origin = window.get_url().origin().unicode_serialization();
|
||||
let filemanager = window.resource_threads().sender();
|
||||
|
||||
let mut files: Vec<Root<File>> = vec![];
|
||||
|
@ -1162,7 +1163,7 @@ impl Activatable for HTMLInputElement {
|
|||
|
||||
if self.Multiple() {
|
||||
let (chan, recv) = ipc::channel().expect("Error initializing channel");
|
||||
let msg = FileManagerThreadMsg::SelectFiles(filter, chan);
|
||||
let msg = FileManagerThreadMsg::SelectFiles(filter, chan, origin);
|
||||
let _ = filemanager.send(msg).unwrap();
|
||||
|
||||
match recv.recv().expect("IpcSender side error") {
|
||||
|
@ -1182,7 +1183,7 @@ impl Activatable for HTMLInputElement {
|
|||
};
|
||||
} else {
|
||||
let (chan, recv) = ipc::channel().expect("Error initializing channel");
|
||||
let msg = FileManagerThreadMsg::SelectFile(filter, chan);
|
||||
let msg = FileManagerThreadMsg::SelectFile(filter, chan, origin);
|
||||
let _ = filemanager.send(msg).unwrap();
|
||||
|
||||
match recv.recv().expect("IpcSender side error") {
|
||||
|
|
|
@ -13,10 +13,9 @@ use dom::bindings::str::{DOMString, USVString};
|
|||
use dom::blob::Blob;
|
||||
use dom::urlhelper::UrlHelper;
|
||||
use dom::urlsearchparams::URLSearchParams;
|
||||
use ipc_channel::ipc;
|
||||
use net_traits::IpcSend;
|
||||
use net_traits::blob_url_store::{BlobURLStoreEntry, BlobURLStoreMsg, parse_blob_url};
|
||||
use net_traits::filemanager_thread::FileManagerThreadMsg;
|
||||
use net_traits::blob_url_store::parse_blob_url;
|
||||
use net_traits::filemanager_thread::{SelectedFileId, FileManagerThreadMsg};
|
||||
use std::borrow::ToOwned;
|
||||
use std::default::Default;
|
||||
use url::quirks::domain_to_unicode;
|
||||
|
@ -125,34 +124,9 @@ impl URL {
|
|||
return DOMString::from(URL::unicode_serialization_blob_url(&origin, &id));
|
||||
}
|
||||
|
||||
let filemanager = global.resource_threads().sender();
|
||||
let id = blob.get_id();
|
||||
|
||||
let slice = blob.get_slice_or_empty();
|
||||
let bytes = slice.get_bytes();
|
||||
|
||||
let entry = BlobURLStoreEntry {
|
||||
type_string: blob.Type().to_string(),
|
||||
filename: None, // XXX: the filename is currently only in File object now
|
||||
size: blob.Size(),
|
||||
bytes: bytes.to_vec(),
|
||||
};
|
||||
|
||||
let (tx, rx) = ipc::channel().unwrap();
|
||||
|
||||
let msg = BlobURLStoreMsg::AddEntry(entry, origin.clone(), tx);
|
||||
|
||||
let _ = filemanager.send(FileManagerThreadMsg::BlobURLStoreMsg(msg));
|
||||
|
||||
match rx.recv().unwrap() {
|
||||
Ok(id) => {
|
||||
DOMString::from(URL::unicode_serialization_blob_url(&origin, &id))
|
||||
}
|
||||
Err(_) => {
|
||||
// Generate a dummy id
|
||||
let id = Uuid::new_v4().simple().to_string();
|
||||
DOMString::from(URL::unicode_serialization_blob_url(&origin, &id))
|
||||
}
|
||||
}
|
||||
DOMString::from(URL::unicode_serialization_blob_url(&origin, &id.0))
|
||||
}
|
||||
|
||||
// https://w3c.github.io/FileAPI/#dfn-revokeObjectURL
|
||||
|
@ -166,13 +140,15 @@ impl URL {
|
|||
|
||||
NOTE: The first step is unnecessary, since closed blobs do not exist in the store
|
||||
*/
|
||||
let origin = global.get_url().origin().unicode_serialization();
|
||||
|
||||
match Url::parse(&url) {
|
||||
Ok(url) => match parse_blob_url(&url) {
|
||||
Some((id, _)) => {
|
||||
let filemanager = global.resource_threads().sender();
|
||||
let msg = BlobURLStoreMsg::DeleteEntry(id.simple().to_string());
|
||||
let _ = filemanager.send(FileManagerThreadMsg::BlobURLStoreMsg(msg));
|
||||
let id = SelectedFileId(id.simple().to_string());
|
||||
let msg = FileManagerThreadMsg::DecRef(id, origin);
|
||||
let _ = filemanager.send(msg);
|
||||
}
|
||||
None => {}
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue