mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Auto merge of #12268 - izgzhen:fm-spawn, r=Manishearth
Spawn threads for requests in file manager 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 <!-- Either: --> - [x] There are tests for these changes OR <!-- 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/12268) <!-- Reviewable:end -->
This commit is contained in:
commit
c2a22bd05e
6 changed files with 217 additions and 164 deletions
|
@ -19,9 +19,9 @@ use std::sync::Arc;
|
||||||
// TODO: Check on GET
|
// TODO: Check on GET
|
||||||
// https://w3c.github.io/FileAPI/#requestResponseModel
|
// https://w3c.github.io/FileAPI/#requestResponseModel
|
||||||
|
|
||||||
pub fn load_blob(load_data: &LoadData, start_chan: LoadConsumer,
|
pub fn load_blob(load_data: LoadData, start_chan: LoadConsumer,
|
||||||
classifier: Arc<MimeClassifier>, opt_filename: Option<String>,
|
classifier: Arc<MimeClassifier>, opt_filename: Option<String>,
|
||||||
rel_pos: &RelativePos, entry: &BlobURLStoreEntry) {
|
rel_pos: RelativePos, entry: BlobURLStoreEntry) {
|
||||||
let content_type: Mime = entry.type_string.parse().unwrap_or(mime!(Text / Plain));
|
let content_type: Mime = entry.type_string.parse().unwrap_or(mime!(Text / Plain));
|
||||||
let charset = content_type.get_param(Attr::Charset);
|
let charset = content_type.get_param(Attr::Charset);
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,13 @@ use net_traits::filemanager_thread::{FileManagerThreadMsg, FileManagerResult, Fi
|
||||||
use net_traits::filemanager_thread::{SelectedFile, RelativePos, FileManagerThreadError, SelectedFileId};
|
use net_traits::filemanager_thread::{SelectedFile, RelativePos, FileManagerThreadError, SelectedFileId};
|
||||||
use net_traits::{LoadConsumer, LoadData, NetworkError};
|
use net_traits::{LoadConsumer, LoadData, NetworkError};
|
||||||
use resource_thread::send_error;
|
use resource_thread::send_error;
|
||||||
use std::cell::Cell;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
use std::ops::Index;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::atomic::{self, AtomicUsize, Ordering};
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||||
use tinyfiledialogs;
|
use tinyfiledialogs;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -96,10 +97,11 @@ struct FileStoreEntry {
|
||||||
/// Backend implementation
|
/// Backend implementation
|
||||||
file_impl: FileImpl,
|
file_impl: FileImpl,
|
||||||
/// Reference counting
|
/// Reference counting
|
||||||
refs: Cell<usize>,
|
refs: AtomicUsize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// File backend implementation
|
/// File backend implementation
|
||||||
|
#[derive(Clone)]
|
||||||
enum FileImpl {
|
enum FileImpl {
|
||||||
PathOnly(PathBuf),
|
PathOnly(PathBuf),
|
||||||
Memory(BlobURLStoreEntry),
|
Memory(BlobURLStoreEntry),
|
||||||
|
@ -108,57 +110,76 @@ enum FileImpl {
|
||||||
|
|
||||||
struct FileManager<UI: 'static + UIProvider> {
|
struct FileManager<UI: 'static + UIProvider> {
|
||||||
receiver: IpcReceiver<FileManagerThreadMsg>,
|
receiver: IpcReceiver<FileManagerThreadMsg>,
|
||||||
store: HashMap<Uuid, FileStoreEntry>,
|
store: Arc<FileManagerStore<UI>>,
|
||||||
classifier: Arc<MimeClassifier>,
|
classifier: Arc<MimeClassifier>,
|
||||||
ui: &'static UI,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<UI: 'static + UIProvider> FileManager<UI> {
|
impl<UI: 'static + UIProvider> FileManager<UI> {
|
||||||
fn new(recv: IpcReceiver<FileManagerThreadMsg>, ui: &'static UI) -> FileManager<UI> {
|
fn new(recv: IpcReceiver<FileManagerThreadMsg>, ui: &'static UI) -> FileManager<UI> {
|
||||||
FileManager {
|
FileManager {
|
||||||
receiver: recv,
|
receiver: recv,
|
||||||
store: HashMap::new(),
|
store: Arc::new(FileManagerStore::new(ui)),
|
||||||
classifier: Arc::new(MimeClassifier::new()),
|
classifier: Arc::new(MimeClassifier::new()),
|
||||||
ui: ui,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start the file manager event loop
|
/// Start the file manager event loop
|
||||||
fn start(&mut self) {
|
fn start(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
|
let store = self.store.clone();
|
||||||
match self.receiver.recv().unwrap() {
|
match self.receiver.recv().unwrap() {
|
||||||
FileManagerThreadMsg::SelectFile(filter, sender, origin) => self.select_file(filter, sender, origin),
|
FileManagerThreadMsg::SelectFile(filter, sender, origin) => {
|
||||||
FileManagerThreadMsg::SelectFiles(filter, sender, origin) => self.select_files(filter, sender, origin),
|
spawn_named("select file".to_owned(), move || {
|
||||||
FileManagerThreadMsg::ReadFile(sender, id, origin) => {
|
store.select_file(filter, sender, origin);
|
||||||
match self.try_read_file(id, origin) {
|
});
|
||||||
Ok(buffer) => { let _ = sender.send(Ok(buffer)); }
|
}
|
||||||
Err(_) => { let _ = sender.send(Err(FileManagerThreadError::ReadFileError)); }
|
FileManagerThreadMsg::SelectFiles(filter, sender, origin) => {
|
||||||
}
|
spawn_named("select files".to_owned(), move || {
|
||||||
|
store.select_files(filter, sender, origin);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
FileManagerThreadMsg::ReadFile(sender, id, origin) => {
|
||||||
|
spawn_named("read file".to_owned(), move || {
|
||||||
|
match store.try_read_file(id, origin) {
|
||||||
|
Ok(buffer) => { let _ = sender.send(Ok(buffer)); }
|
||||||
|
Err(_) => { let _ = sender.send(Err(FileManagerThreadError::ReadFileError)); }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
FileManagerThreadMsg::TransferMemory(entry, sender, origin) => {
|
||||||
|
spawn_named("transfer memory".to_owned(), move || {
|
||||||
|
store.transfer_memory(entry, sender, origin);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
FileManagerThreadMsg::AddSlicedEntry(id, rel_pos, sender, origin) =>{
|
||||||
|
spawn_named("add sliced entry".to_owned(), move || {
|
||||||
|
store.add_sliced_entry(id, rel_pos, sender, origin);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
FileManagerThreadMsg::TransferMemory(entry, sender, origin) =>
|
|
||||||
self.transfer_memory(entry, sender, origin),
|
|
||||||
FileManagerThreadMsg::AddSlicedEntry(id, rel_pos, sender, origin) =>
|
|
||||||
self.add_sliced_entry(id, rel_pos, sender, origin),
|
|
||||||
FileManagerThreadMsg::LoadBlob(load_data, consumer) => {
|
FileManagerThreadMsg::LoadBlob(load_data, consumer) => {
|
||||||
match parse_blob_url(&load_data.url) {
|
match parse_blob_url(&load_data.url.clone()) {
|
||||||
None => {
|
None => {
|
||||||
let e = format!("Invalid blob URL format {:?}", load_data.url);
|
let e = format!("Invalid blob URL format {:?}", load_data.url);
|
||||||
let format_err = NetworkError::Internal(e);
|
let format_err = NetworkError::Internal(e);
|
||||||
send_error(load_data.url.clone(), format_err, consumer);
|
send_error(load_data.url.clone(), format_err, consumer);
|
||||||
}
|
}
|
||||||
Some((id, _fragment)) => {
|
Some((id, _fragment)) => {
|
||||||
self.process_request(&load_data, consumer, &RelativePos::full_range(), &id);
|
self.process_request(load_data, consumer, RelativePos::full_range(), id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
FileManagerThreadMsg::DecRef(id, origin) => {
|
FileManagerThreadMsg::DecRef(id, origin, sender) => {
|
||||||
if let Ok(id) = Uuid::parse_str(&id.0) {
|
if let Ok(id) = Uuid::parse_str(&id.0) {
|
||||||
self.dec_ref(id, origin);
|
spawn_named("dec ref".to_owned(), move || {
|
||||||
|
let _ = sender.send(store.dec_ref(&id, &origin));
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FileManagerThreadMsg::IncRef(id, origin) => {
|
FileManagerThreadMsg::IncRef(id, origin) => {
|
||||||
if let Ok(id) = Uuid::parse_str(&id.0) {
|
if let Ok(id) = Uuid::parse_str(&id.0) {
|
||||||
self.inc_ref(id, origin);
|
spawn_named("inc ref".to_owned(), move || {
|
||||||
|
let _ = store.inc_ref(&id, &origin);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FileManagerThreadMsg::Exit => break,
|
FileManagerThreadMsg::Exit => break,
|
||||||
|
@ -166,51 +187,127 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inc_ref(&mut self, id: Uuid, origin_in: FileOrigin) {
|
fn process_request(&self, load_data: LoadData, consumer: LoadConsumer,
|
||||||
match self.store.get(&id) {
|
rel_pos: RelativePos, id: Uuid) {
|
||||||
Some(entry) => {
|
let origin_in = load_data.url.origin().unicode_serialization();
|
||||||
if entry.origin == origin_in {
|
match self.store.get_impl(&id, &origin_in) {
|
||||||
entry.refs.set(entry.refs.get() + 1);
|
Ok(file_impl) => {
|
||||||
|
match file_impl {
|
||||||
|
FileImpl::Memory(buffered) => {
|
||||||
|
let classifier = self.classifier.clone();
|
||||||
|
spawn_named("load blob".to_owned(), move ||
|
||||||
|
load_blob(load_data, consumer, classifier,
|
||||||
|
None, rel_pos, buffered));
|
||||||
|
}
|
||||||
|
FileImpl::PathOnly(filepath) => {
|
||||||
|
let opt_filename = filepath.file_name()
|
||||||
|
.and_then(|osstr| osstr.to_str())
|
||||||
|
.map(|s| s.to_string());
|
||||||
|
|
||||||
|
let mut bytes = vec![];
|
||||||
|
let mut handler = File::open(&filepath).unwrap();
|
||||||
|
let mime = guess_mime_type_opt(filepath);
|
||||||
|
let size = handler.read_to_end(&mut bytes).unwrap();
|
||||||
|
|
||||||
|
let entry = BlobURLStoreEntry {
|
||||||
|
type_string: match mime {
|
||||||
|
Some(x) => format!("{}", x),
|
||||||
|
None => "".to_string(),
|
||||||
|
},
|
||||||
|
size: size as u64,
|
||||||
|
bytes: bytes,
|
||||||
|
};
|
||||||
|
let classifier = self.classifier.clone();
|
||||||
|
spawn_named("load blob".to_owned(), move ||
|
||||||
|
load_blob(load_data, consumer, classifier,
|
||||||
|
opt_filename, rel_pos, entry));
|
||||||
|
},
|
||||||
|
FileImpl::Sliced(id, rel_pos) => {
|
||||||
|
self.process_request(load_data, consumer, rel_pos, id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => return, // Invalid UUID
|
Err(e) => {
|
||||||
|
send_error(load_data.url.clone(), NetworkError::Internal(format!("{:?}", e)), consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileManagerStore<UI: 'static + UIProvider> {
|
||||||
|
entries: RwLock<HashMap<Uuid, FileStoreEntry>>,
|
||||||
|
ui: &'static UI,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <UI: 'static + UIProvider> FileManagerStore<UI> {
|
||||||
|
fn new(ui: &'static UI) -> Self {
|
||||||
|
FileManagerStore {
|
||||||
|
entries: RwLock::new(HashMap::new()),
|
||||||
|
ui: ui,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_sliced_entry(&mut self, id: SelectedFileId, rel_pos: RelativePos,
|
/// Copy out the file backend implementation content
|
||||||
|
fn get_impl(&self, id: &Uuid, origin_in: &FileOrigin) -> Result<FileImpl, BlobURLStoreError> {
|
||||||
|
match self.entries.read().unwrap().get(id) {
|
||||||
|
Some(ref e) => {
|
||||||
|
if *origin_in != *e.origin {
|
||||||
|
Err(BlobURLStoreError::InvalidOrigin)
|
||||||
|
} else {
|
||||||
|
Ok(e.file_impl.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Err(BlobURLStoreError::InvalidFileID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&self, id: Uuid, entry: FileStoreEntry) {
|
||||||
|
self.entries.write().unwrap().insert(id, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove(&self, id: &Uuid) {
|
||||||
|
self.entries.write().unwrap().remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inc_ref(&self, id: &Uuid, origin_in: &FileOrigin) -> Result<(), BlobURLStoreError>{
|
||||||
|
match self.entries.read().unwrap().get(id) {
|
||||||
|
Some(entry) => {
|
||||||
|
if entry.origin == *origin_in {
|
||||||
|
entry.refs.fetch_add(1, Ordering::Relaxed);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(BlobURLStoreError::InvalidOrigin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Err(BlobURLStoreError::InvalidFileID),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_sliced_entry(&self, parent_id: SelectedFileId, rel_pos: RelativePos,
|
||||||
sender: IpcSender<Result<SelectedFileId, BlobURLStoreError>>,
|
sender: IpcSender<Result<SelectedFileId, BlobURLStoreError>>,
|
||||||
origin_in: FileOrigin) {
|
origin_in: FileOrigin) {
|
||||||
if let Ok(id) = Uuid::parse_str(&id.0) {
|
if let Ok(parent_id) = Uuid::parse_str(&parent_id.0) {
|
||||||
match self.store.get(&id) {
|
match self.inc_ref(&parent_id, &origin_in) {
|
||||||
Some(entry) => {
|
Ok(_) => {
|
||||||
if entry.origin == origin_in {
|
let new_id = Uuid::new_v4();
|
||||||
// inc_ref on parent entry
|
self.insert(new_id, FileStoreEntry {
|
||||||
entry.refs.set(entry.refs.get() + 1);
|
origin: origin_in,
|
||||||
} else {
|
file_impl: FileImpl::Sliced(parent_id, rel_pos),
|
||||||
let _ = sender.send(Err(BlobURLStoreError::InvalidOrigin));
|
refs: AtomicUsize::new(1),
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
},
|
let _ = sender.send(Ok(SelectedFileId(new_id.simple().to_string())));
|
||||||
None => {
|
|
||||||
let _ = sender.send(Err(BlobURLStoreError::InvalidFileID));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
};
|
Err(e) => {
|
||||||
|
let _ = sender.send(Err(e));
|
||||||
let new_id = Uuid::new_v4();
|
}
|
||||||
self.store.insert(new_id, FileStoreEntry {
|
}
|
||||||
origin: origin_in.clone(),
|
|
||||||
file_impl: FileImpl::Sliced(id, rel_pos),
|
|
||||||
refs: Cell::new(1),
|
|
||||||
});
|
|
||||||
|
|
||||||
let _ = sender.send(Ok(SelectedFileId(new_id.simple().to_string())));
|
|
||||||
} else {
|
} else {
|
||||||
let _ = sender.send(Err(BlobURLStoreError::InvalidFileID));
|
let _ = sender.send(Err(BlobURLStoreError::InvalidFileID));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_file(&mut self, patterns: Vec<FilterPattern>,
|
fn select_file(&self, patterns: Vec<FilterPattern>,
|
||||||
sender: IpcSender<FileManagerResult<SelectedFile>>,
|
sender: IpcSender<FileManagerResult<SelectedFile>>,
|
||||||
origin: FileOrigin) {
|
origin: FileOrigin) {
|
||||||
match self.ui.open_file_dialog("", patterns) {
|
match self.ui.open_file_dialog("", patterns) {
|
||||||
|
@ -229,7 +326,7 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_files(&mut self, patterns: Vec<FilterPattern>,
|
fn select_files(&self, patterns: Vec<FilterPattern>,
|
||||||
sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>,
|
sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>,
|
||||||
origin: FileOrigin) {
|
origin: FileOrigin) {
|
||||||
match self.ui.open_file_dialog_multi("", patterns) {
|
match self.ui.open_file_dialog_multi("", patterns) {
|
||||||
|
@ -258,16 +355,16 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_entry(&mut self, file_path: &Path, origin: &str) -> Option<SelectedFile> {
|
fn create_entry(&self, file_path: &Path, origin: &str) -> Option<SelectedFile> {
|
||||||
match File::open(file_path) {
|
match File::open(file_path) {
|
||||||
Ok(handler) => {
|
Ok(handler) => {
|
||||||
let id = Uuid::new_v4();
|
let id = Uuid::new_v4();
|
||||||
let file_impl = FileImpl::PathOnly(file_path.to_path_buf());
|
let file_impl = FileImpl::PathOnly(file_path.to_path_buf());
|
||||||
|
|
||||||
self.store.insert(id, FileStoreEntry {
|
self.insert(id, FileStoreEntry {
|
||||||
origin: origin.to_string(),
|
origin: origin.to_string(),
|
||||||
file_impl: file_impl,
|
file_impl: file_impl,
|
||||||
refs: Cell::new(1),
|
refs: AtomicUsize::new(1),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Unix Epoch: https://doc.servo.org/std/time/constant.UNIX_EPOCH.html
|
// Unix Epoch: https://doc.servo.org/std/time/constant.UNIX_EPOCH.html
|
||||||
|
@ -303,42 +400,40 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_read_file(&self, id: SelectedFileId, origin_in: String) -> Result<Vec<u8>, ()> {
|
fn try_read_file(&self, id: SelectedFileId, origin_in: FileOrigin) -> Result<Vec<u8>, BlobURLStoreError> {
|
||||||
let id = try!(Uuid::parse_str(&id.0).map_err(|_| ()));
|
let id = try!(Uuid::parse_str(&id.0).map_err(|_| BlobURLStoreError::InvalidFileID));
|
||||||
|
|
||||||
match self.store.get(&id) {
|
match self.get_impl(&id, &origin_in) {
|
||||||
Some(entry) => {
|
Ok(file_impl) => {
|
||||||
match entry.file_impl {
|
match file_impl {
|
||||||
FileImpl::PathOnly(ref filepath) => {
|
FileImpl::PathOnly(filepath) => {
|
||||||
if *entry.origin == origin_in {
|
let mut buffer = vec![];
|
||||||
let mut buffer = vec![];
|
let mut handler = try!(File::open(filepath)
|
||||||
let mut handler = try!(File::open(filepath).map_err(|_| ()));
|
.map_err(|_| BlobURLStoreError::InvalidEntry));
|
||||||
try!(handler.read_to_end(&mut buffer).map_err(|_| ()));
|
try!(handler.read_to_end(&mut buffer)
|
||||||
Ok(buffer)
|
.map_err(|_| BlobURLStoreError::External));
|
||||||
} else {
|
Ok(buffer)
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
FileImpl::Memory(ref buffered) => {
|
FileImpl::Memory(buffered) => {
|
||||||
Ok(buffered.bytes.clone())
|
Ok(buffered.bytes)
|
||||||
},
|
},
|
||||||
FileImpl::Sliced(ref id, ref _rel_pos) => {
|
FileImpl::Sliced(id, rel_pos) => {
|
||||||
self.try_read_file(SelectedFileId(id.simple().to_string()), origin_in)
|
self.try_read_file(SelectedFileId(id.simple().to_string()), origin_in)
|
||||||
|
.map(|bytes| bytes.index(rel_pos.to_abs_range(bytes.len())).to_vec())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => Err(()),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dec_ref(&mut self, id: Uuid, origin_in: FileOrigin) {
|
fn dec_ref(&self, id: &Uuid, origin_in: &FileOrigin) -> Result<(), BlobURLStoreError> {
|
||||||
let (is_last_ref, opt_parent_id) = match self.store.get(&id) {
|
let (is_last_ref, opt_parent_id) = match self.entries.read().unwrap().get(id) {
|
||||||
Some(entry) => {
|
Some(entry) => {
|
||||||
if *entry.origin == origin_in {
|
if *entry.origin == *origin_in {
|
||||||
let r = entry.refs.get();
|
let old_refs = entry.refs.fetch_sub(1, Ordering::Release);
|
||||||
|
|
||||||
if r > 1 {
|
if old_refs > 1 {
|
||||||
entry.refs.set(r - 1);
|
|
||||||
(false, None)
|
(false, None)
|
||||||
} else {
|
} else {
|
||||||
if let FileImpl::Sliced(ref parent_id, _) = entry.file_impl {
|
if let FileImpl::Sliced(ref parent_id, _) = entry.file_impl {
|
||||||
|
@ -349,85 +444,33 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // Invalid origin
|
} else { // Invalid origin
|
||||||
return;
|
return Err(BlobURLStoreError::InvalidOrigin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => return, // Invalid UUID
|
None => return Err(BlobURLStoreError::InvalidFileID),
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_last_ref {
|
if is_last_ref {
|
||||||
self.store.remove(&id);
|
atomic::fence(Ordering::Acquire);
|
||||||
|
self.remove(id);
|
||||||
|
|
||||||
if let Some(parent_id) = opt_parent_id {
|
if let Some(parent_id) = opt_parent_id {
|
||||||
self.dec_ref(parent_id, origin_in);
|
return self.dec_ref(&parent_id, origin_in);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_request(&self, load_data: &LoadData, consumer: LoadConsumer,
|
fn transfer_memory(&self, entry: BlobURLStoreEntry,
|
||||||
rel_pos: &RelativePos, id: &Uuid) {
|
sender: IpcSender<Result<SelectedFileId, BlobURLStoreError>>, origin: FileOrigin) {
|
||||||
let origin_in = load_data.url.origin().unicode_serialization();
|
|
||||||
match self.store.get(id) {
|
|
||||||
Some(entry) => {
|
|
||||||
match entry.file_impl {
|
|
||||||
FileImpl::Memory(ref buffered) => {
|
|
||||||
if *entry.origin == origin_in {
|
|
||||||
load_blob(&load_data, consumer, self.classifier.clone(),
|
|
||||||
None, rel_pos, buffered);
|
|
||||||
} else {
|
|
||||||
let e = format!("Invalid blob URL origin {:?}", origin_in);
|
|
||||||
send_error(load_data.url.clone(), NetworkError::Internal(e), consumer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
FileImpl::PathOnly(ref filepath) => {
|
|
||||||
let opt_filename = filepath.file_name()
|
|
||||||
.and_then(|osstr| osstr.to_str())
|
|
||||||
.map(|s| s.to_string());
|
|
||||||
|
|
||||||
if *entry.origin == origin_in {
|
|
||||||
let mut bytes = vec![];
|
|
||||||
let mut handler = File::open(filepath).unwrap();
|
|
||||||
let mime = guess_mime_type_opt(filepath);
|
|
||||||
let size = handler.read_to_end(&mut bytes).unwrap();
|
|
||||||
|
|
||||||
let entry = BlobURLStoreEntry {
|
|
||||||
type_string: match mime {
|
|
||||||
Some(x) => format!("{}", x),
|
|
||||||
None => "".to_string(),
|
|
||||||
},
|
|
||||||
size: size as u64,
|
|
||||||
bytes: bytes,
|
|
||||||
};
|
|
||||||
|
|
||||||
load_blob(&load_data, consumer, self.classifier.clone(),
|
|
||||||
opt_filename, rel_pos, &entry);
|
|
||||||
} else {
|
|
||||||
let e = format!("Invalid blob URL origin {:?}", origin_in);
|
|
||||||
send_error(load_data.url.clone(), NetworkError::Internal(e), consumer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
FileImpl::Sliced(ref id, ref rel_pos) => {
|
|
||||||
self.process_request(load_data, consumer, rel_pos, id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let e = format!("Invalid blob URL key {:?}", id.simple().to_string());
|
|
||||||
send_error(load_data.url.clone(), NetworkError::Internal(e), consumer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transfer_memory(&mut self, entry: BlobURLStoreEntry,
|
|
||||||
sender: IpcSender<Result<SelectedFileId, BlobURLStoreError>>,
|
|
||||||
origin: FileOrigin) {
|
|
||||||
match Url::parse(&origin) { // parse to check sanity
|
match Url::parse(&origin) { // parse to check sanity
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let id = Uuid::new_v4();
|
let id = Uuid::new_v4();
|
||||||
self.store.insert(id, FileStoreEntry {
|
self.insert(id, FileStoreEntry {
|
||||||
origin: origin.clone(),
|
origin: origin.clone(),
|
||||||
file_impl: FileImpl::Memory(entry),
|
file_impl: FileImpl::Memory(entry),
|
||||||
refs: Cell::new(1),
|
refs: AtomicUsize::new(1),
|
||||||
});
|
});
|
||||||
|
|
||||||
let _ = sender.send(Ok(SelectedFileId(id.simple().to_string())));
|
let _ = sender.send(Ok(SelectedFileId(id.simple().to_string())));
|
||||||
|
|
|
@ -13,6 +13,10 @@ pub enum BlobURLStoreError {
|
||||||
InvalidFileID,
|
InvalidFileID,
|
||||||
/// Invalid URL origin
|
/// Invalid URL origin
|
||||||
InvalidOrigin,
|
InvalidOrigin,
|
||||||
|
/// Invalid entry content
|
||||||
|
InvalidEntry,
|
||||||
|
/// External error, from like file system, I/O etc.
|
||||||
|
External,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blob URL store entry, a packaged form of Blob DOM object
|
/// Blob URL store entry, a packaged form of Blob DOM object
|
||||||
|
|
|
@ -136,7 +136,7 @@ pub enum FileManagerThreadMsg {
|
||||||
AddSlicedEntry(SelectedFileId, RelativePos, IpcSender<Result<SelectedFileId, BlobURLStoreError>>, FileOrigin),
|
AddSlicedEntry(SelectedFileId, RelativePos, IpcSender<Result<SelectedFileId, BlobURLStoreError>>, FileOrigin),
|
||||||
|
|
||||||
/// Decrease reference count
|
/// Decrease reference count
|
||||||
DecRef(SelectedFileId, FileOrigin),
|
DecRef(SelectedFileId, FileOrigin, IpcSender<Result<(), BlobURLStoreError>>),
|
||||||
|
|
||||||
/// Increase reference count
|
/// Increase reference count
|
||||||
IncRef(SelectedFileId, FileOrigin),
|
IncRef(SelectedFileId, FileOrigin),
|
||||||
|
|
|
@ -13,6 +13,7 @@ use dom::bindings::str::{DOMString, USVString};
|
||||||
use dom::blob::Blob;
|
use dom::blob::Blob;
|
||||||
use dom::urlhelper::UrlHelper;
|
use dom::urlhelper::UrlHelper;
|
||||||
use dom::urlsearchparams::URLSearchParams;
|
use dom::urlsearchparams::URLSearchParams;
|
||||||
|
use ipc_channel::ipc;
|
||||||
use net_traits::IpcSend;
|
use net_traits::IpcSend;
|
||||||
use net_traits::blob_url_store::parse_blob_url;
|
use net_traits::blob_url_store::parse_blob_url;
|
||||||
use net_traits::filemanager_thread::{SelectedFileId, FileManagerThreadMsg};
|
use net_traits::filemanager_thread::{SelectedFileId, FileManagerThreadMsg};
|
||||||
|
@ -142,17 +143,16 @@ impl URL {
|
||||||
*/
|
*/
|
||||||
let origin = global.get_url().origin().unicode_serialization();
|
let origin = global.get_url().origin().unicode_serialization();
|
||||||
|
|
||||||
match Url::parse(&url) {
|
if let Ok(url) = Url::parse(&url) {
|
||||||
Ok(url) => match parse_blob_url(&url) {
|
if let Some((id, _)) = parse_blob_url(&url) {
|
||||||
Some((id, _)) => {
|
let filemanager = global.resource_threads().sender();
|
||||||
let filemanager = global.resource_threads().sender();
|
let id = SelectedFileId(id.simple().to_string());
|
||||||
let id = SelectedFileId(id.simple().to_string());
|
let (tx, rx) = ipc::channel().unwrap();
|
||||||
let msg = FileManagerThreadMsg::DecRef(id, origin);
|
let msg = FileManagerThreadMsg::DecRef(id, origin, tx);
|
||||||
let _ = filemanager.send(msg);
|
let _ = filemanager.send(msg);
|
||||||
}
|
|
||||||
None => {}
|
let _ = rx.recv().unwrap();
|
||||||
},
|
}
|
||||||
Err(_) => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ fn test_filemanager() {
|
||||||
// Try to select a dummy file "tests/unit/net/test.txt"
|
// Try to select a dummy file "tests/unit/net/test.txt"
|
||||||
let (tx, rx) = ipc::channel().unwrap();
|
let (tx, rx) = ipc::channel().unwrap();
|
||||||
chan.send(FileManagerThreadMsg::SelectFile(patterns.clone(), tx, origin.clone())).unwrap();
|
chan.send(FileManagerThreadMsg::SelectFile(patterns.clone(), tx, origin.clone())).unwrap();
|
||||||
let selected = rx.recv().expect("File manager channel is broken")
|
let selected = rx.recv().expect("Broken channel")
|
||||||
.expect("The file manager failed to find test.txt");
|
.expect("The file manager failed to find test.txt");
|
||||||
|
|
||||||
// Expecting attributes conforming the spec
|
// Expecting attributes conforming the spec
|
||||||
|
@ -53,21 +53,27 @@ fn test_filemanager() {
|
||||||
let (tx2, rx2) = ipc::channel().unwrap();
|
let (tx2, rx2) = ipc::channel().unwrap();
|
||||||
chan.send(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone(), origin.clone())).unwrap();
|
chan.send(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone(), origin.clone())).unwrap();
|
||||||
|
|
||||||
let msg = rx2.recv().expect("File manager channel is broken");
|
let msg = rx2.recv().expect("Broken channel");
|
||||||
|
|
||||||
let vec = msg.expect("File manager reading failure is unexpected");
|
let vec = msg.expect("File manager reading failure is unexpected");
|
||||||
assert!(test_file_content == vec, "Read content differs");
|
assert!(test_file_content == vec, "Read content differs");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the id
|
// Delete the id
|
||||||
chan.send(FileManagerThreadMsg::DecRef(selected.id.clone(), origin.clone())).unwrap();
|
{
|
||||||
|
let (tx2, rx2) = ipc::channel().unwrap();
|
||||||
|
chan.send(FileManagerThreadMsg::DecRef(selected.id.clone(), origin.clone(), tx2)).unwrap();
|
||||||
|
|
||||||
|
let ret = rx2.recv().expect("Broken channel");
|
||||||
|
assert!(ret.is_ok(), "DecRef is not okay");
|
||||||
|
}
|
||||||
|
|
||||||
// Test by reading again, expecting read error because we invalidated the id
|
// Test by reading again, expecting read error because we invalidated the id
|
||||||
{
|
{
|
||||||
let (tx2, rx2) = ipc::channel().unwrap();
|
let (tx2, rx2) = ipc::channel().unwrap();
|
||||||
chan.send(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone(), origin.clone())).unwrap();
|
chan.send(FileManagerThreadMsg::ReadFile(tx2, selected.id.clone(), origin.clone())).unwrap();
|
||||||
|
|
||||||
let msg = rx2.recv().expect("File manager channel is broken");
|
let msg = rx2.recv().expect("Broken channel");
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
Err(FileManagerThreadError::ReadFileError) => {},
|
Err(FileManagerThreadError::ReadFileError) => {},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue