aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/net/filemanager_thread.rs48
-rw-r--r--components/net/resource_thread.rs4
-rw-r--r--components/net_traits/filemanager_thread.rs15
-rw-r--r--components/net_traits/lib.rs22
-rw-r--r--components/script/dom/bindings/trace.rs3
-rw-r--r--components/script/dom/file.rs16
-rw-r--r--components/script/dom/filelist.rs6
-rw-r--r--components/script/dom/htmlformelement.rs2
-rw-r--r--components/script/dom/htmlinputelement.rs68
-rw-r--r--components/script/dom/window.rs1
10 files changed, 155 insertions, 30 deletions
diff --git a/components/net/filemanager_thread.rs b/components/net/filemanager_thread.rs
index 83695434328..f2cdc658b37 100644
--- a/components/net/filemanager_thread.rs
+++ b/components/net/filemanager_thread.rs
@@ -3,7 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
-use net_traits::filemanager_thread::{FileManagerThreadMsg, FileManagerResult, FileManagerThreadError};
+use mime_guess::guess_mime_type_opt;
+use net_traits::filemanager_thread::{FileManagerThreadMsg, FileManagerResult};
+use net_traits::filemanager_thread::{SelectedFile, FileManagerThreadError};
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File;
@@ -17,15 +19,13 @@ pub struct FileManager {
idmap: RefCell<HashMap<Uuid, PathBuf>>,
}
-impl FileManager {
- fn new(recv: IpcReceiver<FileManagerThreadMsg>) -> FileManager {
- FileManager {
- receiver: recv,
- idmap: RefCell::new(HashMap::new()),
- }
- }
+pub trait FileManagerThreadFactory {
+ fn new() -> Self;
+}
- pub fn new_thread() -> IpcSender<FileManagerThreadMsg> {
+impl FileManagerThreadFactory for IpcSender<FileManagerThreadMsg> {
+ /// Create a FileManagerThread
+ fn new() -> IpcSender<FileManagerThreadMsg> {
let (chan, recv) = ipc::channel().unwrap();
spawn_named("FileManager".to_owned(), move || {
@@ -34,6 +34,16 @@ impl FileManager {
chan
}
+}
+
+
+impl FileManager {
+ fn new(recv: IpcReceiver<FileManagerThreadMsg>) -> FileManager {
+ FileManager {
+ receiver: recv,
+ idmap: RefCell::new(HashMap::new()),
+ }
+ }
/// Start the file manager event loop
fn start(&mut self) {
@@ -49,7 +59,7 @@ impl FileManager {
}
impl FileManager {
- fn select_file(&mut self, sender: IpcSender<FileManagerResult<(Uuid, PathBuf, u64)>>) {
+ fn select_file(&mut self, sender: IpcSender<FileManagerResult<SelectedFile>>) {
// TODO: Pull the dialog UI in and get selected
let selected_path = Path::new("");
@@ -63,7 +73,7 @@ impl FileManager {
}
}
- fn select_files(&mut self, sender: IpcSender<FileManagerResult<Vec<(Uuid, PathBuf, u64)>>>) {
+ fn select_files(&mut self, sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>) {
let selected_paths = vec![Path::new("")];
let mut replies = vec![];
@@ -81,7 +91,7 @@ impl FileManager {
let _ = sender.send(Ok(replies));
}
- fn create_entry(&mut self, file_path: &Path) -> Option<(Uuid, PathBuf, u64)> {
+ fn create_entry(&mut self, file_path: &Path) -> Option<SelectedFile> {
match File::open(file_path) {
Ok(handler) => {
let id = Uuid::new_v4();
@@ -100,7 +110,19 @@ impl FileManager {
let filename = file_path.file_name();
match (epoch, filename) {
- (Ok(epoch), Some(filename)) => Some((id, Path::new(filename).to_path_buf(), epoch)),
+ (Ok(epoch), Some(filename)) => {
+ let filename_path = Path::new(filename);
+ let mime = guess_mime_type_opt(filename_path);
+ Some(SelectedFile {
+ id: id,
+ filename: filename_path.to_path_buf(),
+ modified: epoch,
+ type_string: match mime {
+ Some(x) => format!("{}", x),
+ None => "".to_string(),
+ },
+ })
+ }
_ => None
}
},
diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs
index 7e93901a740..43919362bfa 100644
--- a/components/net/resource_thread.rs
+++ b/components/net/resource_thread.rs
@@ -11,6 +11,7 @@ use cookie_storage::CookieStorage;
use data_loader;
use devtools_traits::{DevtoolsControlMsg};
use file_loader;
+use filemanager_thread::FileManagerThreadFactory;
use hsts::HstsList;
use http_loader::{self, HttpState};
use hyper::client::pool::Pool;
@@ -154,7 +155,8 @@ pub fn new_resource_threads(user_agent: String,
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
profiler_chan: ProfilerChan) -> ResourceThreads {
ResourceThreads::new(new_core_resource_thread(user_agent, devtools_chan, profiler_chan),
- StorageThreadFactory::new())
+ StorageThreadFactory::new(),
+ FileManagerThreadFactory::new())
}
diff --git a/components/net_traits/filemanager_thread.rs b/components/net_traits/filemanager_thread.rs
index dfc2d6a5615..ee4fdb291cf 100644
--- a/components/net_traits/filemanager_thread.rs
+++ b/components/net_traits/filemanager_thread.rs
@@ -7,12 +7,21 @@ use std::path::PathBuf;
use uuid::Uuid;
#[derive(Deserialize, Serialize)]
+pub struct SelectedFile {
+ pub id: Uuid,
+ pub filename: PathBuf,
+ pub modified: u64,
+ // https://w3c.github.io/FileAPI/#dfn-type
+ pub type_string: String,
+}
+
+#[derive(Deserialize, Serialize)]
pub enum FileManagerThreadMsg {
/// Select a single file, return triple (FileID, FileName, lastModified)
- SelectFile(IpcSender<FileManagerResult<(Uuid, PathBuf, u64)>>),
+ SelectFile(IpcSender<FileManagerResult<SelectedFile>>),
/// Select multiple files, return a vector of triples
- SelectFiles(IpcSender<FileManagerResult<Vec<(Uuid, PathBuf, u64)>>>),
+ SelectFiles(IpcSender<FileManagerResult<Vec<SelectedFile>>>),
/// Read file, return the bytes
ReadFile(IpcSender<FileManagerResult<Vec<u8>>>, Uuid),
@@ -23,7 +32,7 @@ pub enum FileManagerThreadMsg {
pub type FileManagerResult<T> = Result<T, FileManagerThreadError>;
-#[derive(Deserialize, Serialize)]
+#[derive(Debug, Deserialize, Serialize)]
pub enum FileManagerThreadError {
/// The selection action is invalid, nothing is selected
InvalidSelection,
diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs
index c940f3b0f68..748d8c56c51 100644
--- a/components/net_traits/lib.rs
+++ b/components/net_traits/lib.rs
@@ -28,6 +28,7 @@ extern crate util;
extern crate uuid;
extern crate websocket;
+use filemanager_thread::FileManagerThreadMsg;
use heapsize::HeapSizeOf;
use hyper::header::{ContentType, Headers};
use hyper::http::RawStatus;
@@ -186,8 +187,13 @@ pub type CoreResourceThread = IpcSender<CoreResourceMsg>;
pub type IpcSendResult = Result<(), IOError>;
+/// Abstraction of the ability to send a particular type of message,
+/// used by net_traits::ResourceThreads to ease the use its IpcSender sub-fields
+/// XXX: If this trait will be used more in future, some auto derive might be appealing
pub trait IpcSend<T> where T: serde::Serialize + serde::Deserialize {
+ /// send message T
fn send(&self, T) -> IpcSendResult;
+ /// get underlying sender
fn sender(&self) -> IpcSender<T>;
}
@@ -200,13 +206,17 @@ pub trait IpcSend<T> where T: serde::Serialize + serde::Deserialize {
pub struct ResourceThreads {
core_thread: CoreResourceThread,
storage_thread: IpcSender<StorageThreadMsg>,
+ filemanager_thread: IpcSender<FileManagerThreadMsg>,
}
impl ResourceThreads {
- pub fn new(c: CoreResourceThread, s: IpcSender<StorageThreadMsg>) -> ResourceThreads {
+ pub fn new(c: CoreResourceThread,
+ s: IpcSender<StorageThreadMsg>,
+ f: IpcSender<FileManagerThreadMsg>) -> ResourceThreads {
ResourceThreads {
core_thread: c,
storage_thread: s,
+ filemanager_thread: f,
}
}
}
@@ -231,6 +241,16 @@ impl IpcSend<StorageThreadMsg> for ResourceThreads {
}
}
+impl IpcSend<FileManagerThreadMsg> for ResourceThreads {
+ fn send(&self, msg: FileManagerThreadMsg) -> IpcSendResult {
+ self.filemanager_thread.send(msg)
+ }
+
+ fn sender(&self) -> IpcSender<FileManagerThreadMsg> {
+ self.filemanager_thread.clone()
+ }
+}
+
// Ignore the sub-fields
impl HeapSizeOf for ResourceThreads {
fn heap_size_of_children(&self) -> usize { 0 }
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 1acb5ee9e4b..4c6941a5f5e 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -60,7 +60,7 @@ use net_traits::image::base::{Image, ImageMetadata};
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
use net_traits::response::HttpsState;
use net_traits::storage_thread::StorageType;
-use net_traits::{Metadata, NetworkError};
+use net_traits::{Metadata, NetworkError, ResourceThreads};
use offscreen_gl_context::GLLimits;
use profile_traits::mem::ProfilerChan as MemProfilerChan;
use profile_traits::time::ProfilerChan as TimeProfilerChan;
@@ -321,6 +321,7 @@ no_jsmanaged_fields!(HttpsState);
no_jsmanaged_fields!(SharedRt);
no_jsmanaged_fields!(TouchpadPressurePhase);
no_jsmanaged_fields!(ReferrerPolicy);
+no_jsmanaged_fields!(ResourceThreads);
impl JSTraceable for Box<ScriptChan + Send> {
#[inline]
diff --git a/components/script/dom/file.rs b/components/script/dom/file.rs
index 9667182671a..4ece71a5ce3 100644
--- a/components/script/dom/file.rs
+++ b/components/script/dom/file.rs
@@ -10,6 +10,8 @@ use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::reflect_dom_object;
use dom::blob::{Blob, DataSlice, blob_parts_to_bytes};
+use dom::window::Window;
+use net_traits::filemanager_thread::SelectedFile;
use std::sync::Arc;
use time;
use util::str::DOMString;
@@ -45,6 +47,19 @@ impl File {
FileBinding::Wrap)
}
+ // Construct from selected file message from file manager thread
+ pub fn new_from_selected(window: &Window, selected: SelectedFile) -> Root<File> {
+ let name = DOMString::from(selected.filename.to_str().expect("File name encoding error"));
+
+ // FIXME: fix this after PR #11221 is landed
+ let id = selected.id;
+ let slice = DataSlice::empty();
+
+ let global = GlobalRef::Window(window);
+
+ File::new(global, slice, name, Some(selected.modified as i64), "")
+ }
+
// https://w3c.github.io/FileAPI/#file-constructor
pub fn Constructor(global: GlobalRef,
fileBits: Vec<BlobOrString>,
@@ -64,7 +79,6 @@ impl File {
pub fn name(&self) -> &DOMString {
&self.name
}
-
}
impl FileMethods for File {
diff --git a/components/script/dom/filelist.rs b/components/script/dom/filelist.rs
index ef49e88da6d..bf7243d9e73 100644
--- a/components/script/dom/filelist.rs
+++ b/components/script/dom/filelist.rs
@@ -27,8 +27,10 @@ impl FileList {
}
#[allow(unrooted_must_root)]
- pub fn new(window: &Window, files: Vec<JS<File>>) -> Root<FileList> {
- reflect_dom_object(box FileList::new_inherited(files), GlobalRef::Window(window), FileListBinding::Wrap)
+ pub fn new(window: &Window, files: Vec<Root<File>>) -> Root<FileList> {
+ reflect_dom_object(box FileList::new_inherited(files.iter().map(|r| JS::from_rooted(&r)).collect()),
+ GlobalRef::Window(window),
+ FileListBinding::Wrap)
}
}
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index 92df6695f48..aff02b534b1 100644
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -615,7 +615,7 @@ impl HTMLFormElement {
// Step 4
for datum in &mut ret {
match &*datum.ty {
- "file" | "textarea" => (),
+ "file" | "textarea" => (), // TODO
_ => {
datum.name = clean_crlf(&datum.name);
datum.value = FormDatumValue::String(clean_crlf( match datum.value {
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index c2c76dd16e8..4eb1d4d9ba0 100644
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -7,16 +7,19 @@ use dom::activation::{Activatable, ActivationSource, synthetic_click_activation}
use dom::attr::{Attr, AttrValue};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
+use dom::bindings::codegen::Bindings::FileListBinding::FileListMethods;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods;
use dom::bindings::error::{Error, ErrorResult};
use dom::bindings::inheritance::Castable;
-use dom::bindings::js::{JS, LayoutJS, Root, RootedReference};
+use dom::bindings::js::{JS, LayoutJS, Root, RootedReference, MutNullableHeap};
use dom::document::Document;
use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers, LayoutElementHelpers};
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventtarget::EventTarget;
+use dom::file::File;
+use dom::filelist::FileList;
use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormDatumValue, FormControl, FormDatum, FormSubmitter, HTMLFormElement};
@@ -27,7 +30,9 @@ use dom::node::{document_from_node, window_from_node};
use dom::nodelist::NodeList;
use dom::validation::Validatable;
use dom::virtualmethods::VirtualMethods;
-use ipc_channel::ipc::IpcSender;
+use ipc_channel::ipc::{self, IpcSender};
+use net_traits::IpcSend;
+use net_traits::filemanager_thread::FileManagerThreadMsg;
use script_traits::ScriptMsg as ConstellationMsg;
use std::borrow::ToOwned;
use std::cell::Cell;
@@ -81,7 +86,7 @@ pub struct HTMLInputElement {
// https://html.spec.whatwg.org/multipage/#concept-input-value-dirty-flag
value_dirty: Cell<bool>,
- // TODO: selected files for file input
+ filelist: MutNullableHeap<JS<FileList>>,
}
#[derive(JSTraceable)]
@@ -130,6 +135,7 @@ impl HTMLInputElement {
textinput: DOMRefCell::new(TextInput::new(Single, DOMString::new(), chan, None, SelectionDirection::None)),
activation_state: DOMRefCell::new(InputActivationState::new()),
value_dirty: Cell::new(false),
+ filelist: MutNullableHeap::new(None),
}
}
@@ -354,8 +360,18 @@ impl HTMLInputElementMethods for HTMLInputElement {
|a| DOMString::from(a.summarize().value))
}
ValueMode::Filename => {
- // TODO: return C:\fakepath\<first of selected files> when a file is selected
- DOMString::from("")
+ let mut path = DOMString::from("");
+ match self.filelist.get() {
+ Some(ref fl) => match fl.Item(0) {
+ Some(ref f) => {
+ path.push_str("C:\\fakepath\\");
+ path.push_str(f.name());
+ path
+ }
+ None => path,
+ },
+ None => path,
+ }
}
}
}
@@ -373,7 +389,9 @@ impl HTMLInputElementMethods for HTMLInputElement {
}
ValueMode::Filename => {
if value.is_empty() {
- // TODO: empty list of selected files
+ let window = window_from_node(self);
+ let fl = FileList::new(window.r(), vec![]);
+ self.filelist.set(Some(&fl));
} else {
return Err(Error::InvalidState);
}
@@ -1096,6 +1114,44 @@ impl Activatable for HTMLInputElement {
EventBubbles::Bubbles,
EventCancelable::NotCancelable);
},
+ InputType::InputFile => {
+ let window = window_from_node(self);
+ let filemanager = window.resource_threads().sender();
+
+ let mut files: Vec<Root<File>> = vec![];
+ let mut error = None;
+
+ if self.Multiple() {
+ let (chan, recv) = ipc::channel().expect("Error initializing channel");
+ let msg = FileManagerThreadMsg::SelectFiles(chan);
+ let _ = filemanager.send(msg).unwrap();
+
+ match recv.recv().expect("IpcSender side error") {
+ Ok(selected_files) => {
+ for selected in selected_files {
+ files.push(File::new_from_selected(window.r(), selected));
+ }
+ },
+ Err(err) => error = Some(err),
+ };
+ } else {
+ let (chan, recv) = ipc::channel().expect("Error initializing channel");
+ let msg = FileManagerThreadMsg::SelectFile(chan);
+ let _ = filemanager.send(msg).unwrap();
+
+ match recv.recv().expect("IpcSender side error") {
+ Ok(selected) => files.push(File::new_from_selected(window.r(), selected)),
+ Err(err) => error = Some(err),
+ };
+ }
+
+ if let Some(err) = error {
+ debug!("Input file select error: {:?}", err);
+ } else {
+ let filelist = FileList::new(window.r(), files);
+ self.filelist.set(Some(&filelist));
+ }
+ }
_ => ()
}
}
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index c9111ad15e5..7eab91db3a4 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -1596,4 +1596,3 @@ fn debug_reflow_events(id: PipelineId, goal: &ReflowGoal, query_type: &ReflowQue
println!("{}", debug_msg);
}
-no_jsmanaged_fields!(ResourceThreads);