mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Put Blob URL online
This commit is contained in:
parent
4b78b9adab
commit
fdc3a8e3ac
17 changed files with 260 additions and 191 deletions
|
@ -5,53 +5,92 @@
|
||||||
use hyper::header::{DispositionType, ContentDisposition, DispositionParam};
|
use hyper::header::{DispositionType, ContentDisposition, DispositionParam};
|
||||||
use hyper::header::{Headers, ContentType, ContentLength, Charset};
|
use hyper::header::{Headers, ContentType, ContentLength, Charset};
|
||||||
use hyper::http::RawStatus;
|
use hyper::http::RawStatus;
|
||||||
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use mime::{Mime, Attr};
|
use mime::{Mime, Attr};
|
||||||
use mime_classifier::MimeClassifier;
|
use mime_classifier::MimeClassifier;
|
||||||
use net_traits::ProgressMsg::Done;
|
use net_traits::ProgressMsg::{Payload, Done};
|
||||||
use net_traits::blob_url_store::BlobBuf;
|
use net_traits::blob_url_store::parse_blob_url;
|
||||||
|
use net_traits::filemanager_thread::{FileManagerThreadMsg, SelectedFileId};
|
||||||
use net_traits::response::HttpsState;
|
use net_traits::response::HttpsState;
|
||||||
use net_traits::{LoadConsumer, LoadData, Metadata};
|
use net_traits::{LoadConsumer, LoadData, Metadata, NetworkError};
|
||||||
use resource_thread::start_sending_sniffed_opt;
|
use resource_thread::CancellationListener;
|
||||||
|
use resource_thread::{start_sending_sniffed_opt, send_error};
|
||||||
|
use std::boxed::FnBox;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use util::thread::spawn_named;
|
||||||
|
|
||||||
// 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 factory(filemanager_chan: IpcSender<FileManagerThreadMsg>)
|
||||||
classifier: Arc<MimeClassifier>, blob_buf: BlobBuf) {
|
-> Box<FnBox(LoadData,
|
||||||
let content_type: Mime = blob_buf.type_string.parse().unwrap_or(mime!(Text / Plain));
|
LoadConsumer,
|
||||||
let charset = content_type.get_param(Attr::Charset);
|
Arc<MimeClassifier>,
|
||||||
|
CancellationListener) + Send> {
|
||||||
let mut headers = Headers::new();
|
box move |load_data: LoadData, start_chan, classifier, _cancel_listener| {
|
||||||
|
spawn_named(format!("blob loader for {}", load_data.url), move || {
|
||||||
if let Some(name) = blob_buf.filename {
|
load_blob(load_data, start_chan, classifier, filemanager_chan);
|
||||||
let charset = charset.and_then(|c| c.as_str().parse().ok());
|
})
|
||||||
headers.set(ContentDisposition {
|
}
|
||||||
disposition: DispositionType::Inline,
|
}
|
||||||
parameters: vec![
|
|
||||||
DispositionParam::Filename(charset.unwrap_or(Charset::Us_Ascii),
|
fn load_blob(load_data: LoadData, start_chan: LoadConsumer,
|
||||||
None, name.as_bytes().to_vec())
|
classifier: Arc<MimeClassifier>,
|
||||||
]
|
filemanager_chan: IpcSender<FileManagerThreadMsg>) {
|
||||||
});
|
let (chan, recv) = ipc::channel().unwrap();
|
||||||
}
|
if let Ok((id, origin, _fragment)) = parse_blob_url(&load_data.url.clone()) {
|
||||||
|
let id = SelectedFileId(id.simple().to_string());
|
||||||
headers.set(ContentType(content_type.clone()));
|
let check_url_validity = true;
|
||||||
headers.set(ContentLength(blob_buf.size as u64));
|
let msg = FileManagerThreadMsg::ReadFile(chan, id, check_url_validity, origin);
|
||||||
|
let _ = filemanager_chan.send(msg);
|
||||||
let metadata = Metadata {
|
|
||||||
final_url: load_data.url.clone(),
|
match recv.recv().unwrap() {
|
||||||
content_type: Some(ContentType(content_type.clone())),
|
Ok(blob_buf) => {
|
||||||
charset: charset.map(|c| c.as_str().to_string()),
|
let content_type: Mime = blob_buf.type_string.parse().unwrap_or(mime!(Text / Plain));
|
||||||
headers: Some(headers),
|
let charset = content_type.get_param(Attr::Charset);
|
||||||
// https://w3c.github.io/FileAPI/#TwoHundredOK
|
|
||||||
status: Some(RawStatus(200, "OK".into())),
|
let mut headers = Headers::new();
|
||||||
https_state: HttpsState::None,
|
|
||||||
referrer: None
|
if let Some(name) = blob_buf.filename {
|
||||||
};
|
let charset = charset.and_then(|c| c.as_str().parse().ok());
|
||||||
|
headers.set(ContentDisposition {
|
||||||
if let Ok(chan) =
|
disposition: DispositionType::Inline,
|
||||||
start_sending_sniffed_opt(start_chan, metadata, classifier,
|
parameters: vec![
|
||||||
&blob_buf.bytes, load_data.context.clone()) {
|
DispositionParam::Filename(charset.unwrap_or(Charset::Us_Ascii),
|
||||||
let _ = chan.send(Done(Ok(())));
|
None, name.as_bytes().to_vec())
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
headers.set(ContentType(content_type.clone()));
|
||||||
|
headers.set(ContentLength(blob_buf.size as u64));
|
||||||
|
|
||||||
|
let metadata = Metadata {
|
||||||
|
final_url: load_data.url.clone(),
|
||||||
|
content_type: Some(ContentType(content_type.clone())),
|
||||||
|
charset: charset.map(|c| c.as_str().to_string()),
|
||||||
|
headers: Some(headers),
|
||||||
|
// https://w3c.github.io/FileAPI/#TwoHundredOK
|
||||||
|
status: Some(RawStatus(200, "OK".into())),
|
||||||
|
https_state: HttpsState::None,
|
||||||
|
referrer: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(chan) =
|
||||||
|
start_sending_sniffed_opt(start_chan, metadata, classifier,
|
||||||
|
&blob_buf.bytes, load_data.context.clone()) {
|
||||||
|
let _ = chan.send(Payload(blob_buf.bytes));
|
||||||
|
let _ = chan.send(Done(Ok(())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let err = NetworkError::Internal(format!("{:?}", e));
|
||||||
|
send_error(load_data.url, err, start_chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let e = format!("Invalid blob URL format {:?}", load_data.url);
|
||||||
|
let format_err = NetworkError::Internal(e);
|
||||||
|
send_error(load_data.url.clone(), format_err, start_chan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,11 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use blob_loader::load_blob;
|
|
||||||
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
||||||
use mime_classifier::MimeClassifier;
|
|
||||||
use mime_guess::guess_mime_type_opt;
|
use mime_guess::guess_mime_type_opt;
|
||||||
use net_traits::blob_url_store::{BlobBuf, BlobURLStoreError, parse_blob_url};
|
use net_traits::blob_url_store::{BlobBuf, BlobURLStoreError};
|
||||||
use net_traits::filemanager_thread::{FileManagerThreadMsg, FileManagerResult, FilterPattern, FileOrigin};
|
use net_traits::filemanager_thread::{FileManagerThreadMsg, FileManagerResult, FilterPattern, FileOrigin};
|
||||||
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 resource_thread::send_error;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Seek, SeekFrom};
|
use std::io::{Read, Seek, SeekFrom};
|
||||||
|
@ -133,7 +129,6 @@ enum FileImpl {
|
||||||
struct FileManager<UI: 'static + UIProvider> {
|
struct FileManager<UI: 'static + UIProvider> {
|
||||||
receiver: IpcReceiver<FileManagerThreadMsg>,
|
receiver: IpcReceiver<FileManagerThreadMsg>,
|
||||||
store: Arc<FileManagerStore<UI>>,
|
store: Arc<FileManagerStore<UI>>,
|
||||||
classifier: Arc<MimeClassifier>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<UI: 'static + UIProvider> FileManager<UI> {
|
impl<UI: 'static + UIProvider> FileManager<UI> {
|
||||||
|
@ -141,7 +136,6 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
|
||||||
FileManager {
|
FileManager {
|
||||||
receiver: recv,
|
receiver: recv,
|
||||||
store: Arc::new(FileManagerStore::new(ui)),
|
store: Arc::new(FileManagerStore::new(ui)),
|
||||||
classifier: Arc::new(MimeClassifier::new()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,9 +154,9 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
|
||||||
store.select_files(filter, sender, origin, opt_test_paths);
|
store.select_files(filter, sender, origin, opt_test_paths);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
FileManagerThreadMsg::ReadFile(sender, id, origin) => {
|
FileManagerThreadMsg::ReadFile(sender, id, check_url_validity, origin) => {
|
||||||
spawn_named("read file".to_owned(), move || {
|
spawn_named("read file".to_owned(), move || {
|
||||||
match store.try_read_file(id, origin) {
|
match store.try_read_file(id, check_url_validity, origin) {
|
||||||
Ok(buffer) => { let _ = sender.send(Ok(buffer)); }
|
Ok(buffer) => { let _ = sender.send(Ok(buffer)); }
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = sender.send(Err(FileManagerThreadError::BlobURLStoreError(e)));
|
let _ = sender.send(Err(FileManagerThreadError::BlobURLStoreError(e)));
|
||||||
|
@ -170,9 +164,9 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
FileManagerThreadMsg::PromoteMemory(blob_buf, sender, origin) => {
|
FileManagerThreadMsg::PromoteMemory(blob_buf, set_valid, sender, origin) => {
|
||||||
spawn_named("transfer memory".to_owned(), move || {
|
spawn_named("transfer memory".to_owned(), move || {
|
||||||
store.promote_memory(blob_buf, sender, origin);
|
store.promote_memory(blob_buf, set_valid, sender, origin);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
FileManagerThreadMsg::AddSlicedURLEntry(id, rel_pos, sender, origin) =>{
|
FileManagerThreadMsg::AddSlicedURLEntry(id, rel_pos, sender, origin) =>{
|
||||||
|
@ -180,18 +174,6 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
|
||||||
store.add_sliced_url_entry(id, rel_pos, sender, origin);
|
store.add_sliced_url_entry(id, rel_pos, sender, origin);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
FileManagerThreadMsg::LoadBlob(load_data, consumer) => {
|
|
||||||
match parse_blob_url(&load_data.url.clone()) {
|
|
||||||
None => {
|
|
||||||
let e = format!("Invalid blob URL format {:?}", load_data.url);
|
|
||||||
let format_err = NetworkError::Internal(e);
|
|
||||||
send_error(load_data.url.clone(), format_err, consumer);
|
|
||||||
}
|
|
||||||
Some((id, _fragment)) => {
|
|
||||||
self.process_request(load_data, consumer, id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
FileManagerThreadMsg::RevokeBlobURL(id, origin, sender) => {
|
FileManagerThreadMsg::RevokeBlobURL(id, origin, sender) => {
|
||||||
if let Ok(id) = Uuid::parse_str(&id.0) {
|
if let Ok(id) = Uuid::parse_str(&id.0) {
|
||||||
spawn_named("revoke blob url".to_owned(), move || {
|
spawn_named("revoke blob url".to_owned(), move || {
|
||||||
|
@ -233,18 +215,6 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_request(&self, load_data: LoadData, consumer: LoadConsumer, id: Uuid) {
|
|
||||||
let origin_in = load_data.url.origin().unicode_serialization();
|
|
||||||
// check_url_validity is true since content is requested by this URL
|
|
||||||
match self.store.get_blob_buf(&id, &origin_in, RelativePos::full_range(), true) {
|
|
||||||
Ok(blob_buf) => {
|
|
||||||
let classifier = self.classifier.clone();
|
|
||||||
spawn_named("load blob".to_owned(), move || load_blob(load_data, consumer, classifier, blob_buf));
|
|
||||||
}
|
|
||||||
Err(e) => send_error(load_data.url.clone(), NetworkError::Internal(format!("{:?}", e)), consumer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// File manager's data store. It maintains a thread-safe mapping
|
/// File manager's data store. It maintains a thread-safe mapping
|
||||||
|
@ -271,7 +241,8 @@ impl <UI: 'static + UIProvider> FileManagerStore<UI> {
|
||||||
if *origin_in != *entry.origin {
|
if *origin_in != *entry.origin {
|
||||||
Err(BlobURLStoreError::InvalidOrigin)
|
Err(BlobURLStoreError::InvalidOrigin)
|
||||||
} else {
|
} else {
|
||||||
if check_url_validity && !entry.is_valid_url.load(Ordering::Acquire) {
|
let is_valid = entry.is_valid_url.load(Ordering::Acquire);
|
||||||
|
if check_url_validity && !is_valid {
|
||||||
Err(BlobURLStoreError::InvalidFileID)
|
Err(BlobURLStoreError::InvalidFileID)
|
||||||
} else {
|
} else {
|
||||||
Ok(entry.file_impl.clone())
|
Ok(entry.file_impl.clone())
|
||||||
|
@ -497,44 +468,47 @@ impl <UI: 'static + UIProvider> FileManagerStore<UI> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_read_file(&self, id: SelectedFileId, origin_in: FileOrigin) -> Result<Vec<u8>, BlobURLStoreError> {
|
// Convenient wrapper over get_blob_buf
|
||||||
|
fn try_read_file(&self, id: SelectedFileId, check_url_validity: bool,
|
||||||
|
origin_in: FileOrigin) -> Result<BlobBuf, BlobURLStoreError> {
|
||||||
let id = try!(Uuid::parse_str(&id.0).map_err(|_| BlobURLStoreError::InvalidFileID));
|
let id = try!(Uuid::parse_str(&id.0).map_err(|_| BlobURLStoreError::InvalidFileID));
|
||||||
|
self.get_blob_buf(&id, &origin_in, RelativePos::full_range(), check_url_validity)
|
||||||
// No need to check URL validity in reading a file by FileReader
|
|
||||||
let blob_buf = try!(self.get_blob_buf(&id, &origin_in, RelativePos::full_range(), false));
|
|
||||||
|
|
||||||
Ok(blob_buf.bytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dec_ref(&self, id: &Uuid, origin_in: &FileOrigin,
|
fn dec_ref(&self, id: &Uuid, origin_in: &FileOrigin,
|
||||||
unset_url_validity: bool) -> Result<(), BlobURLStoreError> {
|
unset_url_validity: bool) -> Result<(), BlobURLStoreError> {
|
||||||
let (is_last_ref, opt_parent_id) = match self.entries.read().unwrap().get(id) {
|
let (do_remove, 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 old_refs = entry.refs.fetch_sub(1, Ordering::Release);
|
let old_refs = entry.refs.fetch_sub(1, Ordering::Release);
|
||||||
|
|
||||||
if old_refs > 1 {
|
if unset_url_validity {
|
||||||
if unset_url_validity {
|
entry.is_valid_url.store(false, Ordering::Release);
|
||||||
entry.is_valid_url.store(false, Ordering::Release);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if old_refs > 1 {
|
||||||
|
// not the last reference, no need to touch parent
|
||||||
(false, None)
|
(false, None)
|
||||||
} else {
|
} else {
|
||||||
|
// last reference, and if it has a reference to parent id
|
||||||
|
// dec_ref on parent later if necessary
|
||||||
|
let is_valid = entry.is_valid_url.load(Ordering::Acquire);
|
||||||
if let FileImpl::Sliced(ref parent_id, _) = entry.file_impl {
|
if let FileImpl::Sliced(ref parent_id, _) = entry.file_impl {
|
||||||
// if it has a reference to parent id, dec_ref on parent later
|
(!is_valid, Some(parent_id.clone()))
|
||||||
(true, Some(parent_id.clone()))
|
|
||||||
} else {
|
} else {
|
||||||
(true, None)
|
(!is_valid, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // Invalid origin
|
} else {
|
||||||
return Err(BlobURLStoreError::InvalidOrigin);
|
return Err(BlobURLStoreError::InvalidOrigin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => return Err(BlobURLStoreError::InvalidFileID),
|
None => return Err(BlobURLStoreError::InvalidFileID),
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_last_ref {
|
// Trigger removing if its last reference is gone and it is
|
||||||
|
// not a part of a valid Blob URL
|
||||||
|
if do_remove {
|
||||||
atomic::fence(Ordering::Acquire);
|
atomic::fence(Ordering::Acquire);
|
||||||
self.remove(id);
|
self.remove(id);
|
||||||
|
|
||||||
|
@ -548,7 +522,7 @@ impl <UI: 'static + UIProvider> FileManagerStore<UI> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn promote_memory(&self, blob_buf: BlobBuf,
|
fn promote_memory(&self, blob_buf: BlobBuf, set_valid: bool,
|
||||||
sender: IpcSender<Result<SelectedFileId, BlobURLStoreError>>, origin: FileOrigin) {
|
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(_) => {
|
||||||
|
@ -558,7 +532,7 @@ impl <UI: 'static + UIProvider> FileManagerStore<UI> {
|
||||||
file_impl: FileImpl::Memory(blob_buf),
|
file_impl: FileImpl::Memory(blob_buf),
|
||||||
refs: AtomicUsize::new(1),
|
refs: AtomicUsize::new(1),
|
||||||
// Valid here since PromoteMemory implies URL creation
|
// Valid here since PromoteMemory implies URL creation
|
||||||
is_valid_url: AtomicBool::new(true),
|
is_valid_url: AtomicBool::new(set_valid),
|
||||||
});
|
});
|
||||||
|
|
||||||
let _ = sender.send(Ok(SelectedFileId(id.simple().to_string())));
|
let _ = sender.send(Ok(SelectedFileId(id.simple().to_string())));
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
//! A thread that takes a URL and streams back the binary data.
|
//! A thread that takes a URL and streams back the binary data.
|
||||||
use about_loader;
|
use about_loader;
|
||||||
|
use blob_loader;
|
||||||
use chrome_loader;
|
use chrome_loader;
|
||||||
use connector::{Connector, create_http_connector};
|
use connector::{Connector, create_http_connector};
|
||||||
use content_blocker::BLOCKED_CONTENT_RULES;
|
use content_blocker::BLOCKED_CONTENT_RULES;
|
||||||
|
@ -173,18 +174,20 @@ fn start_sending_opt(start_chan: LoadConsumer, metadata: Metadata,
|
||||||
pub fn new_resource_threads(user_agent: String,
|
pub fn new_resource_threads(user_agent: String,
|
||||||
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||||
profiler_chan: ProfilerChan) -> (ResourceThreads, ResourceThreads) {
|
profiler_chan: ProfilerChan) -> (ResourceThreads, ResourceThreads) {
|
||||||
let (public_core, private_core) = new_core_resource_thread(user_agent, devtools_chan, profiler_chan);
|
let filemanager_chan: IpcSender<FileManagerThreadMsg> = FileManagerThreadFactory::new(TFD_PROVIDER);
|
||||||
|
let (public_core, private_core) = new_core_resource_thread(user_agent, devtools_chan,
|
||||||
|
profiler_chan, filemanager_chan.clone());
|
||||||
let storage: IpcSender<StorageThreadMsg> = StorageThreadFactory::new();
|
let storage: IpcSender<StorageThreadMsg> = StorageThreadFactory::new();
|
||||||
let filemanager: IpcSender<FileManagerThreadMsg> = FileManagerThreadFactory::new(TFD_PROVIDER);
|
(ResourceThreads::new(public_core, storage.clone(), filemanager_chan.clone()),
|
||||||
(ResourceThreads::new(public_core, storage.clone(), filemanager.clone()),
|
ResourceThreads::new(private_core, storage, filemanager_chan))
|
||||||
ResourceThreads::new(private_core, storage, filemanager))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Create a CoreResourceThread
|
/// Create a CoreResourceThread
|
||||||
pub fn new_core_resource_thread(user_agent: String,
|
pub fn new_core_resource_thread(user_agent: String,
|
||||||
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||||
profiler_chan: ProfilerChan)
|
profiler_chan: ProfilerChan,
|
||||||
|
filemanager_chan: IpcSender<FileManagerThreadMsg>)
|
||||||
-> (CoreResourceThread, CoreResourceThread) {
|
-> (CoreResourceThread, CoreResourceThread) {
|
||||||
let (public_setup_chan, public_setup_port) = ipc::channel().unwrap();
|
let (public_setup_chan, public_setup_port) = ipc::channel().unwrap();
|
||||||
let (private_setup_chan, private_setup_port) = ipc::channel().unwrap();
|
let (private_setup_chan, private_setup_port) = ipc::channel().unwrap();
|
||||||
|
@ -192,7 +195,7 @@ pub fn new_core_resource_thread(user_agent: String,
|
||||||
let private_setup_chan_clone = private_setup_chan.clone();
|
let private_setup_chan_clone = private_setup_chan.clone();
|
||||||
spawn_named("ResourceManager".to_owned(), move || {
|
spawn_named("ResourceManager".to_owned(), move || {
|
||||||
let resource_manager = CoreResourceManager::new(
|
let resource_manager = CoreResourceManager::new(
|
||||||
user_agent, devtools_chan, profiler_chan
|
user_agent, devtools_chan, profiler_chan, filemanager_chan
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut channel_manager = ResourceChannelManager {
|
let mut channel_manager = ResourceChannelManager {
|
||||||
|
@ -462,6 +465,7 @@ pub struct CoreResourceManager {
|
||||||
mime_classifier: Arc<MimeClassifier>,
|
mime_classifier: Arc<MimeClassifier>,
|
||||||
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||||
profiler_chan: ProfilerChan,
|
profiler_chan: ProfilerChan,
|
||||||
|
filemanager_chan: IpcSender<FileManagerThreadMsg>,
|
||||||
cancel_load_map: HashMap<ResourceId, Sender<()>>,
|
cancel_load_map: HashMap<ResourceId, Sender<()>>,
|
||||||
next_resource_id: ResourceId,
|
next_resource_id: ResourceId,
|
||||||
}
|
}
|
||||||
|
@ -469,12 +473,14 @@ pub struct CoreResourceManager {
|
||||||
impl CoreResourceManager {
|
impl CoreResourceManager {
|
||||||
pub fn new(user_agent: String,
|
pub fn new(user_agent: String,
|
||||||
devtools_channel: Option<Sender<DevtoolsControlMsg>>,
|
devtools_channel: Option<Sender<DevtoolsControlMsg>>,
|
||||||
profiler_chan: ProfilerChan) -> CoreResourceManager {
|
profiler_chan: ProfilerChan,
|
||||||
|
filemanager_chan: IpcSender<FileManagerThreadMsg>) -> CoreResourceManager {
|
||||||
CoreResourceManager {
|
CoreResourceManager {
|
||||||
user_agent: user_agent,
|
user_agent: user_agent,
|
||||||
mime_classifier: Arc::new(MimeClassifier::new()),
|
mime_classifier: Arc::new(MimeClassifier::new()),
|
||||||
devtools_chan: devtools_channel,
|
devtools_chan: devtools_channel,
|
||||||
profiler_chan: profiler_chan,
|
profiler_chan: profiler_chan,
|
||||||
|
filemanager_chan: filemanager_chan,
|
||||||
cancel_load_map: HashMap::new(),
|
cancel_load_map: HashMap::new(),
|
||||||
next_resource_id: ResourceId(0),
|
next_resource_id: ResourceId(0),
|
||||||
}
|
}
|
||||||
|
@ -548,6 +554,7 @@ impl CoreResourceManager {
|
||||||
},
|
},
|
||||||
"data" => from_factory(data_loader::factory),
|
"data" => from_factory(data_loader::factory),
|
||||||
"about" => from_factory(about_loader::factory),
|
"about" => from_factory(about_loader::factory),
|
||||||
|
"blob" => blob_loader::factory(self.filemanager_chan.clone()),
|
||||||
_ => {
|
_ => {
|
||||||
debug!("resource_thread: no loader for scheme {}", load_data.url.scheme());
|
debug!("resource_thread: no loader for scheme {}", load_data.url.scheme());
|
||||||
send_error(load_data.url, NetworkError::Internal("no loader for scheme".to_owned()), consumer);
|
send_error(load_data.url, NetworkError::Internal("no loader for scheme".to_owned()), consumer);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use filemanager_thread::FileOrigin;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -20,7 +21,7 @@ pub enum BlobURLStoreError {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Standalone blob buffer object
|
/// Standalone blob buffer object
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct BlobBuf {
|
pub struct BlobBuf {
|
||||||
pub filename: Option<String>,
|
pub filename: Option<String>,
|
||||||
/// MIME type string
|
/// MIME type string
|
||||||
|
@ -33,13 +34,25 @@ pub struct BlobBuf {
|
||||||
|
|
||||||
/// Parse URL as Blob URL scheme's definition
|
/// Parse URL as Blob URL scheme's definition
|
||||||
/// https://w3c.github.io/FileAPI/#DefinitionOfScheme
|
/// https://w3c.github.io/FileAPI/#DefinitionOfScheme
|
||||||
pub fn parse_blob_url(url: &Url) -> Option<(Uuid, Option<&str>)> {
|
pub fn parse_blob_url(url: &Url) -> Result<(Uuid, FileOrigin, Option<String>), ()> {
|
||||||
url.path_segments().and_then(|mut segments| {
|
let url_inner = try!(Url::parse(url.path()).map_err(|_| ()));
|
||||||
let id_str = match (segments.next(), segments.next()) {
|
let fragment = url_inner.fragment().map(|s| s.to_string());
|
||||||
(Some(s), None) => s,
|
let mut segs = try!(url_inner.path_segments().ok_or(()));
|
||||||
_ => return None,
|
let id = try!(segs.nth(0).ok_or(()));
|
||||||
};
|
let id = try!(Uuid::from_str(id).map_err(|_| ()));
|
||||||
|
Ok((id, get_blob_origin(&url_inner), fragment))
|
||||||
Uuid::from_str(id_str).map(|id| (id, url.fragment())).ok()
|
}
|
||||||
})
|
|
||||||
|
/// Given an URL, returning the Origin that a Blob created under this
|
||||||
|
/// URL should have.
|
||||||
|
/// HACK(izgzhen): Not well-specified on spec, and it is a bit a hack
|
||||||
|
/// both due to ambiguity of spec and that we have to serialization the
|
||||||
|
/// Origin here.
|
||||||
|
pub fn get_blob_origin(url: &Url) -> FileOrigin {
|
||||||
|
if url.scheme() == "file" {
|
||||||
|
// NOTE: by default this is "null" (Opaque), which is not ideal
|
||||||
|
"file://".to_string()
|
||||||
|
} else {
|
||||||
|
url.origin().unicode_serialization()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ use num_traits::ToPrimitive;
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use super::{LoadConsumer, LoadData};
|
|
||||||
|
|
||||||
// HACK: Not really process-safe now, we should send Origin
|
// HACK: Not really process-safe now, we should send Origin
|
||||||
// directly instead of this in future, blocked on #11722
|
// directly instead of this in future, blocked on #11722
|
||||||
|
@ -128,15 +127,13 @@ pub enum FileManagerThreadMsg {
|
||||||
/// Select multiple files, return a vector of triples
|
/// Select multiple files, return a vector of triples
|
||||||
SelectFiles(Vec<FilterPattern>, IpcSender<FileManagerResult<Vec<SelectedFile>>>, FileOrigin, Option<Vec<String>>),
|
SelectFiles(Vec<FilterPattern>, IpcSender<FileManagerResult<Vec<SelectedFile>>>, FileOrigin, Option<Vec<String>>),
|
||||||
|
|
||||||
/// Read file, return the bytes
|
/// Read file by FileID, optionally check URL validity based on
|
||||||
ReadFile(IpcSender<FileManagerResult<Vec<u8>>>, SelectedFileId, FileOrigin),
|
/// third flag, return the blob buffer object
|
||||||
|
ReadFile(IpcSender<FileManagerResult<BlobBuf>>, SelectedFileId, bool, FileOrigin),
|
||||||
/// Load resource by Blob URL
|
|
||||||
LoadBlob(LoadData, LoadConsumer),
|
|
||||||
|
|
||||||
/// Add an entry as promoted memory-based blob and send back the associated FileID
|
/// Add an entry as promoted memory-based blob and send back the associated FileID
|
||||||
/// as part of a valid Blob URL
|
/// as part of a valid/invalid Blob URL depending on the second bool flag
|
||||||
PromoteMemory(BlobBuf, IpcSender<Result<SelectedFileId, BlobURLStoreError>>, FileOrigin),
|
PromoteMemory(BlobBuf, bool, IpcSender<Result<SelectedFileId, BlobURLStoreError>>, FileOrigin),
|
||||||
|
|
||||||
/// Add a sliced entry pointing to the parent FileID, and send back the associated FileID
|
/// Add a sliced entry pointing to the parent FileID, and send back the associated FileID
|
||||||
/// as part of a valid Blob URL
|
/// as part of a valid Blob URL
|
||||||
|
|
|
@ -15,7 +15,7 @@ use encoding::all::UTF_8;
|
||||||
use encoding::types::{EncoderTrap, Encoding};
|
use encoding::types::{EncoderTrap, Encoding};
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use net_traits::IpcSend;
|
use net_traits::IpcSend;
|
||||||
use net_traits::blob_url_store::BlobBuf;
|
use net_traits::blob_url_store::{BlobBuf, get_blob_origin};
|
||||||
use net_traits::filemanager_thread::{FileManagerThreadMsg, SelectedFileId, RelativePos};
|
use net_traits::filemanager_thread::{FileManagerThreadMsg, SelectedFileId, RelativePos};
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
@ -174,7 +174,7 @@ impl Blob {
|
||||||
match *self.blob_impl.borrow() {
|
match *self.blob_impl.borrow() {
|
||||||
BlobImpl::File(ref f) => {
|
BlobImpl::File(ref f) => {
|
||||||
let global = self.global();
|
let global = self.global();
|
||||||
let origin = global.r().get_url().origin().unicode_serialization();
|
let origin = get_blob_origin(&global.r().get_url());
|
||||||
let filemanager = global.r().resource_threads().sender();
|
let filemanager = global.r().resource_threads().sender();
|
||||||
let (tx, rx) = ipc::channel().unwrap();
|
let (tx, rx) = ipc::channel().unwrap();
|
||||||
|
|
||||||
|
@ -185,7 +185,9 @@ impl Blob {
|
||||||
Err(_) => SelectedFileId("".to_string()) // Return a dummy id on error
|
Err(_) => SelectedFileId("".to_string()) // Return a dummy id on error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BlobImpl::Memory(ref slice) => self.promote_to_file(slice),
|
BlobImpl::Memory(ref slice) => {
|
||||||
|
self.promote(slice, true)
|
||||||
|
}
|
||||||
BlobImpl::Sliced(ref parent, ref rel_pos) => {
|
BlobImpl::Sliced(ref parent, ref rel_pos) => {
|
||||||
match *parent.blob_impl.borrow() {
|
match *parent.blob_impl.borrow() {
|
||||||
BlobImpl::Sliced(_, _) => {
|
BlobImpl::Sliced(_, _) => {
|
||||||
|
@ -196,7 +198,7 @@ impl Blob {
|
||||||
BlobImpl::File(ref f) =>
|
BlobImpl::File(ref f) =>
|
||||||
self.create_sliced_url_id(&f.id, rel_pos),
|
self.create_sliced_url_id(&f.id, rel_pos),
|
||||||
BlobImpl::Memory(ref bytes) => {
|
BlobImpl::Memory(ref bytes) => {
|
||||||
let parent_id = parent.promote_to_file(bytes);
|
let parent_id = parent.promote(bytes, false);
|
||||||
*self.blob_impl.borrow_mut() = BlobImpl::Sliced(parent.clone(), rel_pos.clone());
|
*self.blob_impl.borrow_mut() = BlobImpl::Sliced(parent.clone(), rel_pos.clone());
|
||||||
self.create_sliced_url_id(&parent_id, rel_pos)
|
self.create_sliced_url_id(&parent_id, rel_pos)
|
||||||
}
|
}
|
||||||
|
@ -206,10 +208,12 @@ impl Blob {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Promite memory-based Blob to file-based,
|
/// Promite memory-based Blob to file-based,
|
||||||
/// The bytes in data slice will be transferred to file manager thread
|
/// The bytes in data slice will be transferred to file manager thread.
|
||||||
fn promote_to_file(&self, bytes: &[u8]) -> SelectedFileId {
|
/// Depending on set_valid, the returned FileID can be part of
|
||||||
|
/// valid or invalid Blob URL.
|
||||||
|
fn promote(&self, bytes: &[u8], set_valid: bool) -> SelectedFileId {
|
||||||
let global = self.global();
|
let global = self.global();
|
||||||
let origin = global.r().get_url().origin().unicode_serialization();
|
let origin = get_blob_origin(&global.r().get_url());
|
||||||
let filemanager = global.r().resource_threads().sender();
|
let filemanager = global.r().resource_threads().sender();
|
||||||
|
|
||||||
let blob_buf = BlobBuf {
|
let blob_buf = BlobBuf {
|
||||||
|
@ -220,7 +224,7 @@ impl Blob {
|
||||||
};
|
};
|
||||||
|
|
||||||
let (tx, rx) = ipc::channel().unwrap();
|
let (tx, rx) = ipc::channel().unwrap();
|
||||||
let _ = filemanager.send(FileManagerThreadMsg::PromoteMemory(blob_buf, tx, origin.clone()));
|
let _ = filemanager.send(FileManagerThreadMsg::PromoteMemory(blob_buf, set_valid, tx, origin.clone()));
|
||||||
|
|
||||||
match rx.recv().unwrap() {
|
match rx.recv().unwrap() {
|
||||||
Ok(new_id) => SelectedFileId(new_id.0),
|
Ok(new_id) => SelectedFileId(new_id.0),
|
||||||
|
@ -234,7 +238,7 @@ impl Blob {
|
||||||
rel_pos: &RelativePos) -> SelectedFileId {
|
rel_pos: &RelativePos) -> SelectedFileId {
|
||||||
let global = self.global();
|
let global = self.global();
|
||||||
|
|
||||||
let origin = global.r().get_url().origin().unicode_serialization();
|
let origin = get_blob_origin(&global.r().get_url());
|
||||||
|
|
||||||
let filemanager = global.r().resource_threads().sender();
|
let filemanager = global.r().resource_threads().sender();
|
||||||
let (tx, rx) = ipc::channel().unwrap();
|
let (tx, rx) = ipc::channel().unwrap();
|
||||||
|
@ -252,7 +256,7 @@ impl Blob {
|
||||||
fn clean_up_file_resource(&self) {
|
fn clean_up_file_resource(&self) {
|
||||||
if let BlobImpl::File(ref f) = *self.blob_impl.borrow() {
|
if let BlobImpl::File(ref f) = *self.blob_impl.borrow() {
|
||||||
let global = self.global();
|
let global = self.global();
|
||||||
let origin = global.r().get_url().origin().unicode_serialization();
|
let origin = get_blob_origin(&global.r().get_url());
|
||||||
|
|
||||||
let filemanager = global.r().resource_threads().sender();
|
let filemanager = global.r().resource_threads().sender();
|
||||||
let (tx, rx) = ipc::channel().unwrap();
|
let (tx, rx) = ipc::channel().unwrap();
|
||||||
|
@ -275,19 +279,15 @@ impl Drop for Blob {
|
||||||
fn read_file(global: GlobalRef, id: SelectedFileId) -> Result<Vec<u8>, ()> {
|
fn read_file(global: GlobalRef, id: SelectedFileId) -> Result<Vec<u8>, ()> {
|
||||||
let file_manager = global.filemanager_thread();
|
let file_manager = global.filemanager_thread();
|
||||||
let (chan, recv) = ipc::channel().map_err(|_|())?;
|
let (chan, recv) = ipc::channel().map_err(|_|())?;
|
||||||
let origin = global.get_url().origin().unicode_serialization();
|
let origin = get_blob_origin(&global.get_url());
|
||||||
let msg = FileManagerThreadMsg::ReadFile(chan, id, origin);
|
let check_url_validity = false;
|
||||||
|
let msg = FileManagerThreadMsg::ReadFile(chan, id, check_url_validity, origin);
|
||||||
let _ = file_manager.send(msg);
|
let _ = file_manager.send(msg);
|
||||||
|
|
||||||
let result = match recv.recv() {
|
match recv.recv().unwrap() {
|
||||||
Ok(ret) => ret,
|
Ok(blob_buf) => Ok(blob_buf.bytes),
|
||||||
Err(e) => {
|
Err(_) => Err(()),
|
||||||
debug!("File manager thread has problem {:?}", e);
|
}
|
||||||
return Err(())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
result.map_err(|_|())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract bytes from BlobParts, used by Blob and File constructor
|
/// Extract bytes from BlobParts, used by Blob and File constructor
|
||||||
|
@ -389,7 +389,7 @@ fn is_ascii_printable(string: &str) -> bool {
|
||||||
/// Bump the reference counter in file manager thread
|
/// Bump the reference counter in file manager thread
|
||||||
fn inc_ref_id(global: GlobalRef, id: SelectedFileId) {
|
fn inc_ref_id(global: GlobalRef, id: SelectedFileId) {
|
||||||
let file_manager = global.filemanager_thread();
|
let file_manager = global.filemanager_thread();
|
||||||
let origin = global.get_url().origin().unicode_serialization();
|
let origin = get_blob_origin(&global.get_url());
|
||||||
let msg = FileManagerThreadMsg::IncRef(id, origin);
|
let msg = FileManagerThreadMsg::IncRef(id, origin);
|
||||||
let _ = file_manager.send(msg);
|
let _ = file_manager.send(msg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ use ipc_channel::ipc::{self, IpcSender};
|
||||||
use mime_guess;
|
use mime_guess;
|
||||||
use msg::constellation_msg::Key;
|
use msg::constellation_msg::Key;
|
||||||
use net_traits::IpcSend;
|
use net_traits::IpcSend;
|
||||||
|
use net_traits::blob_url_store::get_blob_origin;
|
||||||
use net_traits::filemanager_thread::{FileManagerThreadMsg, FilterPattern};
|
use net_traits::filemanager_thread::{FileManagerThreadMsg, FilterPattern};
|
||||||
use script_traits::ScriptMsg as ConstellationMsg;
|
use script_traits::ScriptMsg as ConstellationMsg;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
|
@ -749,7 +750,7 @@ impl HTMLInputElement {
|
||||||
// Select files by invoking UI or by passed in argument
|
// Select files by invoking UI or by passed in argument
|
||||||
fn select_files(&self, opt_test_paths: Option<Vec<DOMString>>) {
|
fn select_files(&self, opt_test_paths: Option<Vec<DOMString>>) {
|
||||||
let window = window_from_node(self);
|
let window = window_from_node(self);
|
||||||
let origin = window.get_url().origin().unicode_serialization();
|
let origin = get_blob_origin(&window.get_url());
|
||||||
let filemanager = window.resource_threads().sender();
|
let filemanager = window.resource_threads().sender();
|
||||||
|
|
||||||
let mut files: Vec<Root<File>> = vec![];
|
let mut files: Vec<Root<File>> = vec![];
|
||||||
|
@ -770,13 +771,6 @@ impl HTMLInputElement {
|
||||||
for selected in selected_files {
|
for selected in selected_files {
|
||||||
files.push(File::new_from_selected(window.r(), selected));
|
files.push(File::new_from_selected(window.r(), selected));
|
||||||
}
|
}
|
||||||
|
|
||||||
target.fire_event("input",
|
|
||||||
EventBubbles::Bubbles,
|
|
||||||
EventCancelable::NotCancelable);
|
|
||||||
target.fire_event("change",
|
|
||||||
EventBubbles::Bubbles,
|
|
||||||
EventCancelable::NotCancelable);
|
|
||||||
},
|
},
|
||||||
Err(err) => error = Some(err),
|
Err(err) => error = Some(err),
|
||||||
};
|
};
|
||||||
|
@ -799,13 +793,6 @@ impl HTMLInputElement {
|
||||||
match recv.recv().expect("IpcSender side error") {
|
match recv.recv().expect("IpcSender side error") {
|
||||||
Ok(selected) => {
|
Ok(selected) => {
|
||||||
files.push(File::new_from_selected(window.r(), selected));
|
files.push(File::new_from_selected(window.r(), selected));
|
||||||
|
|
||||||
target.fire_event("input",
|
|
||||||
EventBubbles::Bubbles,
|
|
||||||
EventCancelable::NotCancelable);
|
|
||||||
target.fire_event("change",
|
|
||||||
EventBubbles::Bubbles,
|
|
||||||
EventCancelable::NotCancelable);
|
|
||||||
},
|
},
|
||||||
Err(err) => error = Some(err),
|
Err(err) => error = Some(err),
|
||||||
};
|
};
|
||||||
|
@ -816,6 +803,13 @@ impl HTMLInputElement {
|
||||||
} else {
|
} else {
|
||||||
let filelist = FileList::new(window.r(), files);
|
let filelist = FileList::new(window.r(), files);
|
||||||
self.filelist.set(Some(&filelist));
|
self.filelist.set(Some(&filelist));
|
||||||
|
|
||||||
|
target.fire_event("input",
|
||||||
|
EventBubbles::Bubbles,
|
||||||
|
EventCancelable::NotCancelable);
|
||||||
|
target.fire_event("change",
|
||||||
|
EventBubbles::Bubbles,
|
||||||
|
EventCancelable::NotCancelable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,8 @@ use dom::urlhelper::UrlHelper;
|
||||||
use dom::urlsearchparams::URLSearchParams;
|
use dom::urlsearchparams::URLSearchParams;
|
||||||
use ipc_channel::ipc;
|
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::{get_blob_origin, parse_blob_url};
|
||||||
use net_traits::filemanager_thread::{FileOrigin, SelectedFileId, FileManagerThreadMsg};
|
use net_traits::filemanager_thread::{SelectedFileId, FileManagerThreadMsg};
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use url::quirks::domain_to_unicode;
|
use url::quirks::domain_to_unicode;
|
||||||
|
@ -117,7 +117,7 @@ impl URL {
|
||||||
pub fn CreateObjectURL(global: GlobalRef, blob: &Blob) -> DOMString {
|
pub fn CreateObjectURL(global: GlobalRef, blob: &Blob) -> DOMString {
|
||||||
/// XXX: Second field is an unicode-serialized Origin, it is a temporary workaround
|
/// XXX: Second field is an unicode-serialized Origin, it is a temporary workaround
|
||||||
/// and should not be trusted. See issue https://github.com/servo/servo/issues/11722
|
/// and should not be trusted. See issue https://github.com/servo/servo/issues/11722
|
||||||
let origin = URL::get_blob_origin(&global.get_url());
|
let origin = get_blob_origin(&global.get_url());
|
||||||
|
|
||||||
if blob.IsClosed() {
|
if blob.IsClosed() {
|
||||||
// Generate a dummy id
|
// Generate a dummy id
|
||||||
|
@ -141,10 +141,10 @@ impl URL {
|
||||||
|
|
||||||
NOTE: The first step is unnecessary, since closed blobs do not exist in the store
|
NOTE: The first step is unnecessary, since closed blobs do not exist in the store
|
||||||
*/
|
*/
|
||||||
let origin = global.get_url().origin().unicode_serialization();
|
let origin = get_blob_origin(&global.get_url());
|
||||||
|
|
||||||
if let Ok(url) = Url::parse(&url) {
|
if let Ok(url) = Url::parse(&url) {
|
||||||
if let Some((id, _)) = parse_blob_url(&url) {
|
if let Ok((id, _, _)) = parse_blob_url(&url) {
|
||||||
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 (tx, rx) = ipc::channel().unwrap();
|
||||||
|
@ -172,18 +172,6 @@ impl URL {
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NOTE(izgzhen): WebKit will return things like blob:file:///XXX
|
|
||||||
while Chrome will return blob:null/XXX
|
|
||||||
This is not well-specified, and I prefer the WebKit way here
|
|
||||||
*/
|
|
||||||
fn get_blob_origin(url: &Url) -> FileOrigin {
|
|
||||||
if url.scheme() == "file" {
|
|
||||||
"file://".to_string()
|
|
||||||
} else {
|
|
||||||
url.origin().unicode_serialization()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl URLMethods for URL {
|
impl URLMethods for URL {
|
||||||
|
|
|
@ -368,6 +368,12 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
|
||||||
// Step 11 - abort existing requests
|
// Step 11 - abort existing requests
|
||||||
self.terminate_ongoing_fetch();
|
self.terminate_ongoing_fetch();
|
||||||
|
|
||||||
|
// TODO(izgzhen): In the WPT test: FileAPI/blob/Blob-XHR-revoke.html,
|
||||||
|
// the xhr.open(url) is expected to hold a reference to the URL,
|
||||||
|
// thus renders following revocations invalid. Though we won't
|
||||||
|
// implement this for now, if ever needed, we should check blob
|
||||||
|
// scheme and trigger corresponding actions here.
|
||||||
|
|
||||||
// Step 12
|
// Step 12
|
||||||
*self.request_method.borrow_mut() = parsed_method;
|
*self.request_method.borrow_mut() = parsed_method;
|
||||||
*self.request_url.borrow_mut() = Some(parsed_url);
|
*self.request_url.borrow_mut() = Some(parsed_url);
|
||||||
|
|
|
@ -52,12 +52,12 @@ fn test_filemanager() {
|
||||||
// Test by reading, expecting same content
|
// Test by reading, expecting same content
|
||||||
{
|
{
|
||||||
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(), false, origin.clone())).unwrap();
|
||||||
|
|
||||||
let msg = rx2.recv().expect("Broken channel");
|
let msg = rx2.recv().expect("Broken channel");
|
||||||
|
|
||||||
let vec = msg.expect("File manager reading failure is unexpected");
|
let blob_buf = msg.expect("File manager reading failure is unexpected");
|
||||||
assert_eq!(test_file_content, vec, "Read content differs");
|
assert_eq!(test_file_content, blob_buf.bytes, "Read content differs");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the id
|
// Delete the id
|
||||||
|
@ -72,7 +72,7 @@ fn test_filemanager() {
|
||||||
// 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(), false, origin.clone())).unwrap();
|
||||||
|
|
||||||
let msg = rx2.recv().expect("Broken channel");
|
let msg = rx2.recv().expect("Broken channel");
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use msg::constellation_msg::{PipelineId, ReferrerPolicy};
|
use msg::constellation_msg::{PipelineId, ReferrerPolicy};
|
||||||
|
use net::filemanager_thread::{FileManagerThreadFactory, TFDProvider};
|
||||||
use net::resource_thread::new_core_resource_thread;
|
use net::resource_thread::new_core_resource_thread;
|
||||||
use net_traits::hosts::{parse_hostsfile, host_replacement};
|
use net_traits::hosts::{parse_hostsfile, host_replacement};
|
||||||
use net_traits::{CoreResourceMsg, LoadData, LoadConsumer, LoadContext};
|
use net_traits::{CoreResourceMsg, LoadData, LoadConsumer, LoadContext};
|
||||||
|
@ -15,6 +16,8 @@ use std::net::IpAddr;
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
const TFD_PROVIDER: &'static TFDProvider = &TFDProvider;
|
||||||
|
|
||||||
fn ip(s: &str) -> IpAddr {
|
fn ip(s: &str) -> IpAddr {
|
||||||
s.parse().unwrap()
|
s.parse().unwrap()
|
||||||
}
|
}
|
||||||
|
@ -40,7 +43,8 @@ impl LoadOrigin for ResourceTest {
|
||||||
fn test_exit() {
|
fn test_exit() {
|
||||||
let (tx, _rx) = ipc::channel().unwrap();
|
let (tx, _rx) = ipc::channel().unwrap();
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
let (resource_thread, _) = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx));
|
let filemanager_chan = FileManagerThreadFactory::new(TFD_PROVIDER);
|
||||||
|
let (resource_thread, _) = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx), filemanager_chan);
|
||||||
resource_thread.send(CoreResourceMsg::Exit(sender)).unwrap();
|
resource_thread.send(CoreResourceMsg::Exit(sender)).unwrap();
|
||||||
receiver.recv().unwrap();
|
receiver.recv().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -49,7 +53,8 @@ fn test_exit() {
|
||||||
fn test_bad_scheme() {
|
fn test_bad_scheme() {
|
||||||
let (tx, _rx) = ipc::channel().unwrap();
|
let (tx, _rx) = ipc::channel().unwrap();
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
let (resource_thread, _) = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx));
|
let filemanager_chan = FileManagerThreadFactory::new(TFD_PROVIDER);
|
||||||
|
let (resource_thread, _) = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx), filemanager_chan);
|
||||||
let (start_chan, start) = ipc::channel().unwrap();
|
let (start_chan, start) = ipc::channel().unwrap();
|
||||||
let url = Url::parse("bogus://whatever").unwrap();
|
let url = Url::parse("bogus://whatever").unwrap();
|
||||||
resource_thread.send(CoreResourceMsg::Load(LoadData::new(LoadContext::Browsing, url, &ResourceTest),
|
resource_thread.send(CoreResourceMsg::Load(LoadData::new(LoadContext::Browsing, url, &ResourceTest),
|
||||||
|
@ -228,7 +233,8 @@ fn test_cancelled_listener() {
|
||||||
|
|
||||||
let (tx, _rx) = ipc::channel().unwrap();
|
let (tx, _rx) = ipc::channel().unwrap();
|
||||||
let (exit_sender, exit_receiver) = ipc::channel().unwrap();
|
let (exit_sender, exit_receiver) = ipc::channel().unwrap();
|
||||||
let (resource_thread, _) = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx));
|
let filemanager_chan = FileManagerThreadFactory::new(TFD_PROVIDER);
|
||||||
|
let (resource_thread, _) = new_core_resource_thread("".to_owned(), None, ProfilerChan(tx), filemanager_chan);
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
let (id_sender, id_receiver) = ipc::channel().unwrap();
|
let (id_sender, id_receiver) = ipc::channel().unwrap();
|
||||||
let (sync_sender, sync_receiver) = ipc::channel().unwrap();
|
let (sync_sender, sync_receiver) = ipc::channel().unwrap();
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[url_xmlhttprequest_img.html]
|
|
||||||
type: reftest
|
|
||||||
reftype: ==
|
|
||||||
refurl: /FileAPI/url/url_xmlhttprequest_img-ref.html
|
|
||||||
expected: FAIL
|
|
|
@ -1,6 +0,0 @@
|
||||||
[Blob-url.html]
|
|
||||||
type: testharness
|
|
||||||
expected: TIMEOUT
|
|
||||||
[Worker supports Blob url]
|
|
||||||
expected: TIMEOUT
|
|
||||||
|
|
|
@ -5782,6 +5782,18 @@
|
||||||
"url": "/_mozilla/css/word_break_a.html"
|
"url": "/_mozilla/css/word_break_a.html"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"mozilla/blob_url_upload.html": [
|
||||||
|
{
|
||||||
|
"path": "mozilla/blob_url_upload.html",
|
||||||
|
"references": [
|
||||||
|
[
|
||||||
|
"/_mozilla/mozilla/blob_url_upload_ref.html",
|
||||||
|
"=="
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"url": "/_mozilla/mozilla/blob_url_upload.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
"mozilla/canvas/drawimage_html_image_1.html": [
|
"mozilla/canvas/drawimage_html_image_1.html": [
|
||||||
{
|
{
|
||||||
"path": "mozilla/canvas/drawimage_html_image_1.html",
|
"path": "mozilla/canvas/drawimage_html_image_1.html",
|
||||||
|
@ -14852,6 +14864,18 @@
|
||||||
"url": "/_mozilla/css/word_break_a.html"
|
"url": "/_mozilla/css/word_break_a.html"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"mozilla/blob_url_upload.html": [
|
||||||
|
{
|
||||||
|
"path": "mozilla/blob_url_upload.html",
|
||||||
|
"references": [
|
||||||
|
[
|
||||||
|
"/_mozilla/mozilla/blob_url_upload_ref.html",
|
||||||
|
"=="
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"url": "/_mozilla/mozilla/blob_url_upload.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
"mozilla/canvas/drawimage_html_image_1.html": [
|
"mozilla/canvas/drawimage_html_image_1.html": [
|
||||||
{
|
{
|
||||||
"path": "mozilla/canvas/drawimage_html_image_1.html",
|
"path": "mozilla/canvas/drawimage_html_image_1.html",
|
||||||
|
|
3
tests/wpt/mozilla/meta/mozilla/blob_url_upload.html.ini
Normal file
3
tests/wpt/mozilla/meta/mozilla/blob_url_upload.html.ini
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[blob_url_upload.html]
|
||||||
|
type: reftest
|
||||||
|
prefs: [dom.testing.htmlinputelement.select_files.enabled:true]
|
22
tests/wpt/mozilla/tests/mozilla/blob_url_upload.html
Normal file
22
tests/wpt/mozilla/tests/mozilla/blob_url_upload.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Blob URL with File Upload</title>
|
||||||
|
<link rel="match" href="blob_url_upload_ref.html">
|
||||||
|
<body>
|
||||||
|
<img src="" id="image">
|
||||||
|
<input type="file" id="file-input"">
|
||||||
|
</body>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var image = document.getElementById("image");
|
||||||
|
|
||||||
|
var inputElem = document.getElementById("file-input");
|
||||||
|
|
||||||
|
inputElem.selectFiles(["./tests/wpt/mozilla/tests/mozilla/test.jpg"]);
|
||||||
|
|
||||||
|
var f = inputElem.files[0];
|
||||||
|
|
||||||
|
var url = URL.createObjectURL(f);
|
||||||
|
|
||||||
|
image.src = url;
|
||||||
|
|
||||||
|
</script>
|
7
tests/wpt/mozilla/tests/mozilla/blob_url_upload_ref.html
Normal file
7
tests/wpt/mozilla/tests/mozilla/blob_url_upload_ref.html
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Reference: Blob URL with File Upload</title>
|
||||||
|
<body>
|
||||||
|
<img src="test.jpg" id="image">
|
||||||
|
<input type="file" id="file-input"">
|
||||||
|
</body>
|
Loading…
Add table
Add a link
Reference in a new issue