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:
bors-servo 2016-05-23 01:10:46 -07:00
commit 7cea4eb01c
10 changed files with 155 additions and 30 deletions

View file

@ -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]

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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 {

View file

@ -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));
}
}
_ => ()
}
}

View file

@ -1603,4 +1603,3 @@ fn debug_reflow_events(id: PipelineId, goal: &ReflowGoal, query_type: &ReflowQue
println!("{}", debug_msg);
}
no_jsmanaged_fields!(ResourceThreads);