mirror of
https://github.com/servo/servo.git
synced 2025-06-08 00:23:30 +00:00
Auto merge of #11225 - izgzhen:patch-input-element-file, r=Manishearth
Implement file related functionalities in htmlinputelement and related - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy --faster` does not report any errors - [x] These changes is related to #11131 - [x] These changes do not require tests because it is a partial implementation 1. Improve the `filemanager_thread` by adding type string and create `SelectedFile` 2. Fill several gaps in `htmlinputelement` implementation related to file type 3. Improve the `File` interface to accommodate the above changes 4. Integrate changes introduced by PR #11189 <!-- 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/11225) <!-- Reviewable:end -->
This commit is contained in:
commit
7cea4eb01c
10 changed files with 155 additions and 30 deletions
|
@ -3,7 +3,9 @@
|
||||||
* 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 ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
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::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
@ -17,15 +19,13 @@ pub struct FileManager {
|
||||||
idmap: RefCell<HashMap<Uuid, PathBuf>>,
|
idmap: RefCell<HashMap<Uuid, PathBuf>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileManager {
|
pub trait FileManagerThreadFactory {
|
||||||
fn new(recv: IpcReceiver<FileManagerThreadMsg>) -> FileManager {
|
fn new() -> Self;
|
||||||
FileManager {
|
}
|
||||||
receiver: recv,
|
|
||||||
idmap: RefCell::new(HashMap::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_thread() -> IpcSender<FileManagerThreadMsg> {
|
impl FileManagerThreadFactory for IpcSender<FileManagerThreadMsg> {
|
||||||
|
/// Create a FileManagerThread
|
||||||
|
fn new() -> IpcSender<FileManagerThreadMsg> {
|
||||||
let (chan, recv) = ipc::channel().unwrap();
|
let (chan, recv) = ipc::channel().unwrap();
|
||||||
|
|
||||||
spawn_named("FileManager".to_owned(), move || {
|
spawn_named("FileManager".to_owned(), move || {
|
||||||
|
@ -34,6 +34,16 @@ impl FileManager {
|
||||||
|
|
||||||
chan
|
chan
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl FileManager {
|
||||||
|
fn new(recv: IpcReceiver<FileManagerThreadMsg>) -> FileManager {
|
||||||
|
FileManager {
|
||||||
|
receiver: recv,
|
||||||
|
idmap: RefCell::new(HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Start the file manager event loop
|
/// Start the file manager event loop
|
||||||
fn start(&mut self) {
|
fn start(&mut self) {
|
||||||
|
@ -49,7 +59,7 @@ impl FileManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// TODO: Pull the dialog UI in and get selected
|
||||||
let selected_path = Path::new("");
|
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 selected_paths = vec![Path::new("")];
|
||||||
|
|
||||||
let mut replies = vec![];
|
let mut replies = vec![];
|
||||||
|
@ -81,7 +91,7 @@ impl FileManager {
|
||||||
let _ = sender.send(Ok(replies));
|
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) {
|
match File::open(file_path) {
|
||||||
Ok(handler) => {
|
Ok(handler) => {
|
||||||
let id = Uuid::new_v4();
|
let id = Uuid::new_v4();
|
||||||
|
@ -100,7 +110,19 @@ impl FileManager {
|
||||||
let filename = file_path.file_name();
|
let filename = file_path.file_name();
|
||||||
|
|
||||||
match (epoch, filename) {
|
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
|
_ => None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,6 +11,7 @@ use cookie_storage::CookieStorage;
|
||||||
use data_loader;
|
use data_loader;
|
||||||
use devtools_traits::{DevtoolsControlMsg};
|
use devtools_traits::{DevtoolsControlMsg};
|
||||||
use file_loader;
|
use file_loader;
|
||||||
|
use filemanager_thread::FileManagerThreadFactory;
|
||||||
use hsts::HstsList;
|
use hsts::HstsList;
|
||||||
use http_loader::{self, HttpState};
|
use http_loader::{self, HttpState};
|
||||||
use hyper::client::pool::Pool;
|
use hyper::client::pool::Pool;
|
||||||
|
@ -154,7 +155,8 @@ pub fn new_resource_threads(user_agent: String,
|
||||||
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
devtools_chan: Option<Sender<DevtoolsControlMsg>>,
|
||||||
profiler_chan: ProfilerChan) -> ResourceThreads {
|
profiler_chan: ProfilerChan) -> ResourceThreads {
|
||||||
ResourceThreads::new(new_core_resource_thread(user_agent, devtools_chan, profiler_chan),
|
ResourceThreads::new(new_core_resource_thread(user_agent, devtools_chan, profiler_chan),
|
||||||
StorageThreadFactory::new())
|
StorageThreadFactory::new(),
|
||||||
|
FileManagerThreadFactory::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,22 @@ use ipc_channel::ipc::IpcSender;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use uuid::Uuid;
|
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)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub enum FileManagerThreadMsg {
|
pub enum FileManagerThreadMsg {
|
||||||
/// Select a single file, return triple (FileID, FileName, lastModified)
|
/// 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
|
/// 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
|
/// Read file, return the bytes
|
||||||
ReadFile(IpcSender<FileManagerResult<Vec<u8>>>, Uuid),
|
ReadFile(IpcSender<FileManagerResult<Vec<u8>>>, Uuid),
|
||||||
|
@ -23,7 +32,7 @@ pub enum FileManagerThreadMsg {
|
||||||
|
|
||||||
pub type FileManagerResult<T> = Result<T, FileManagerThreadError>;
|
pub type FileManagerResult<T> = Result<T, FileManagerThreadError>;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub enum FileManagerThreadError {
|
pub enum FileManagerThreadError {
|
||||||
/// The selection action is invalid, nothing is selected
|
/// The selection action is invalid, nothing is selected
|
||||||
InvalidSelection,
|
InvalidSelection,
|
||||||
|
|
|
@ -28,6 +28,7 @@ extern crate util;
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
extern crate websocket;
|
extern crate websocket;
|
||||||
|
|
||||||
|
use filemanager_thread::FileManagerThreadMsg;
|
||||||
use heapsize::HeapSizeOf;
|
use heapsize::HeapSizeOf;
|
||||||
use hyper::header::{ContentType, Headers};
|
use hyper::header::{ContentType, Headers};
|
||||||
use hyper::http::RawStatus;
|
use hyper::http::RawStatus;
|
||||||
|
@ -217,8 +218,13 @@ pub type CoreResourceThread = IpcSender<CoreResourceMsg>;
|
||||||
|
|
||||||
pub type IpcSendResult = Result<(), IOError>;
|
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 {
|
pub trait IpcSend<T> where T: serde::Serialize + serde::Deserialize {
|
||||||
|
/// send message T
|
||||||
fn send(&self, T) -> IpcSendResult;
|
fn send(&self, T) -> IpcSendResult;
|
||||||
|
/// get underlying sender
|
||||||
fn sender(&self) -> IpcSender<T>;
|
fn sender(&self) -> IpcSender<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,13 +237,17 @@ pub trait IpcSend<T> where T: serde::Serialize + serde::Deserialize {
|
||||||
pub struct ResourceThreads {
|
pub struct ResourceThreads {
|
||||||
core_thread: CoreResourceThread,
|
core_thread: CoreResourceThread,
|
||||||
storage_thread: IpcSender<StorageThreadMsg>,
|
storage_thread: IpcSender<StorageThreadMsg>,
|
||||||
|
filemanager_thread: IpcSender<FileManagerThreadMsg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResourceThreads {
|
impl ResourceThreads {
|
||||||
pub fn new(c: CoreResourceThread, s: IpcSender<StorageThreadMsg>) -> ResourceThreads {
|
pub fn new(c: CoreResourceThread,
|
||||||
|
s: IpcSender<StorageThreadMsg>,
|
||||||
|
f: IpcSender<FileManagerThreadMsg>) -> ResourceThreads {
|
||||||
ResourceThreads {
|
ResourceThreads {
|
||||||
core_thread: c,
|
core_thread: c,
|
||||||
storage_thread: s,
|
storage_thread: s,
|
||||||
|
filemanager_thread: f,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,6 +272,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
|
// Ignore the sub-fields
|
||||||
impl HeapSizeOf for ResourceThreads {
|
impl HeapSizeOf for ResourceThreads {
|
||||||
fn heap_size_of_children(&self) -> usize { 0 }
|
fn heap_size_of_children(&self) -> usize { 0 }
|
||||||
|
|
|
@ -60,7 +60,7 @@ use net_traits::image::base::{Image, ImageMetadata};
|
||||||
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
|
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
|
||||||
use net_traits::response::HttpsState;
|
use net_traits::response::HttpsState;
|
||||||
use net_traits::storage_thread::StorageType;
|
use net_traits::storage_thread::StorageType;
|
||||||
use net_traits::{Metadata, NetworkError};
|
use net_traits::{Metadata, NetworkError, ResourceThreads};
|
||||||
use offscreen_gl_context::GLLimits;
|
use offscreen_gl_context::GLLimits;
|
||||||
use profile_traits::mem::ProfilerChan as MemProfilerChan;
|
use profile_traits::mem::ProfilerChan as MemProfilerChan;
|
||||||
use profile_traits::time::ProfilerChan as TimeProfilerChan;
|
use profile_traits::time::ProfilerChan as TimeProfilerChan;
|
||||||
|
@ -321,6 +321,7 @@ no_jsmanaged_fields!(HttpsState);
|
||||||
no_jsmanaged_fields!(SharedRt);
|
no_jsmanaged_fields!(SharedRt);
|
||||||
no_jsmanaged_fields!(TouchpadPressurePhase);
|
no_jsmanaged_fields!(TouchpadPressurePhase);
|
||||||
no_jsmanaged_fields!(ReferrerPolicy);
|
no_jsmanaged_fields!(ReferrerPolicy);
|
||||||
|
no_jsmanaged_fields!(ResourceThreads);
|
||||||
|
|
||||||
impl JSTraceable for Box<ScriptChan + Send> {
|
impl JSTraceable for Box<ScriptChan + Send> {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -10,6 +10,8 @@ use dom::bindings::global::GlobalRef;
|
||||||
use dom::bindings::js::Root;
|
use dom::bindings::js::Root;
|
||||||
use dom::bindings::reflector::reflect_dom_object;
|
use dom::bindings::reflector::reflect_dom_object;
|
||||||
use dom::blob::{Blob, DataSlice, blob_parts_to_bytes};
|
use dom::blob::{Blob, DataSlice, blob_parts_to_bytes};
|
||||||
|
use dom::window::Window;
|
||||||
|
use net_traits::filemanager_thread::SelectedFile;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use time;
|
use time;
|
||||||
use util::str::DOMString;
|
use util::str::DOMString;
|
||||||
|
@ -45,6 +47,19 @@ impl File {
|
||||||
FileBinding::Wrap)
|
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
|
// https://w3c.github.io/FileAPI/#file-constructor
|
||||||
pub fn Constructor(global: GlobalRef,
|
pub fn Constructor(global: GlobalRef,
|
||||||
fileBits: Vec<BlobOrString>,
|
fileBits: Vec<BlobOrString>,
|
||||||
|
@ -64,7 +79,6 @@ impl File {
|
||||||
pub fn name(&self) -> &DOMString {
|
pub fn name(&self) -> &DOMString {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileMethods for File {
|
impl FileMethods for File {
|
||||||
|
|
|
@ -27,8 +27,10 @@ impl FileList {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unrooted_must_root)]
|
#[allow(unrooted_must_root)]
|
||||||
pub fn new(window: &Window, files: Vec<JS<File>>) -> Root<FileList> {
|
pub fn new(window: &Window, files: Vec<Root<File>>) -> Root<FileList> {
|
||||||
reflect_dom_object(box FileList::new_inherited(files), GlobalRef::Window(window), FileListBinding::Wrap)
|
reflect_dom_object(box FileList::new_inherited(files.iter().map(|r| JS::from_rooted(&r)).collect()),
|
||||||
|
GlobalRef::Window(window),
|
||||||
|
FileListBinding::Wrap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -615,7 +615,7 @@ impl HTMLFormElement {
|
||||||
// Step 4
|
// Step 4
|
||||||
for datum in &mut ret {
|
for datum in &mut ret {
|
||||||
match &*datum.ty {
|
match &*datum.ty {
|
||||||
"file" | "textarea" => (),
|
"file" | "textarea" => (), // TODO
|
||||||
_ => {
|
_ => {
|
||||||
datum.name = clean_crlf(&datum.name);
|
datum.name = clean_crlf(&datum.name);
|
||||||
datum.value = FormDatumValue::String(clean_crlf( match datum.value {
|
datum.value = FormDatumValue::String(clean_crlf( match datum.value {
|
||||||
|
|
|
@ -7,16 +7,19 @@ use dom::activation::{Activatable, ActivationSource, synthetic_click_activation}
|
||||||
use dom::attr::{Attr, AttrValue};
|
use dom::attr::{Attr, AttrValue};
|
||||||
use dom::bindings::cell::DOMRefCell;
|
use dom::bindings::cell::DOMRefCell;
|
||||||
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
|
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;
|
||||||
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
|
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
|
||||||
use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods;
|
use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods;
|
||||||
use dom::bindings::error::{Error, ErrorResult};
|
use dom::bindings::error::{Error, ErrorResult};
|
||||||
use dom::bindings::inheritance::Castable;
|
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::document::Document;
|
||||||
use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers, LayoutElementHelpers};
|
use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers, LayoutElementHelpers};
|
||||||
use dom::event::{Event, EventBubbles, EventCancelable};
|
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||||
use dom::eventtarget::EventTarget;
|
use dom::eventtarget::EventTarget;
|
||||||
|
use dom::file::File;
|
||||||
|
use dom::filelist::FileList;
|
||||||
use dom::htmlelement::HTMLElement;
|
use dom::htmlelement::HTMLElement;
|
||||||
use dom::htmlfieldsetelement::HTMLFieldSetElement;
|
use dom::htmlfieldsetelement::HTMLFieldSetElement;
|
||||||
use dom::htmlformelement::{FormDatumValue, FormControl, FormDatum, FormSubmitter, HTMLFormElement};
|
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::nodelist::NodeList;
|
||||||
use dom::validation::Validatable;
|
use dom::validation::Validatable;
|
||||||
use dom::virtualmethods::VirtualMethods;
|
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 script_traits::ScriptMsg as ConstellationMsg;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
@ -81,7 +86,7 @@ pub struct HTMLInputElement {
|
||||||
// https://html.spec.whatwg.org/multipage/#concept-input-value-dirty-flag
|
// https://html.spec.whatwg.org/multipage/#concept-input-value-dirty-flag
|
||||||
value_dirty: Cell<bool>,
|
value_dirty: Cell<bool>,
|
||||||
|
|
||||||
// TODO: selected files for file input
|
filelist: MutNullableHeap<JS<FileList>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(JSTraceable)]
|
#[derive(JSTraceable)]
|
||||||
|
@ -130,6 +135,7 @@ impl HTMLInputElement {
|
||||||
textinput: DOMRefCell::new(TextInput::new(Single, DOMString::new(), chan, None, SelectionDirection::None)),
|
textinput: DOMRefCell::new(TextInput::new(Single, DOMString::new(), chan, None, SelectionDirection::None)),
|
||||||
activation_state: DOMRefCell::new(InputActivationState::new()),
|
activation_state: DOMRefCell::new(InputActivationState::new()),
|
||||||
value_dirty: Cell::new(false),
|
value_dirty: Cell::new(false),
|
||||||
|
filelist: MutNullableHeap::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,8 +360,18 @@ impl HTMLInputElementMethods for HTMLInputElement {
|
||||||
|a| DOMString::from(a.summarize().value))
|
|a| DOMString::from(a.summarize().value))
|
||||||
}
|
}
|
||||||
ValueMode::Filename => {
|
ValueMode::Filename => {
|
||||||
// TODO: return C:\fakepath\<first of selected files> when a file is selected
|
let mut path = DOMString::from("");
|
||||||
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 => {
|
ValueMode::Filename => {
|
||||||
if value.is_empty() {
|
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 {
|
} else {
|
||||||
return Err(Error::InvalidState);
|
return Err(Error::InvalidState);
|
||||||
}
|
}
|
||||||
|
@ -1096,6 +1114,44 @@ impl Activatable for HTMLInputElement {
|
||||||
EventBubbles::Bubbles,
|
EventBubbles::Bubbles,
|
||||||
EventCancelable::NotCancelable);
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1603,4 +1603,3 @@ fn debug_reflow_events(id: PipelineId, goal: &ReflowGoal, query_type: &ReflowQue
|
||||||
println!("{}", debug_msg);
|
println!("{}", debug_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
no_jsmanaged_fields!(ResourceThreads);
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue