aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2016-07-09 02:02:34 -0700
committerGitHub <noreply@github.com>2016-07-09 02:02:34 -0700
commit8cb05a36bc1a20ec6373ddb23ce127c7801ba5d6 (patch)
tree85a294c6d17493671656f7f32db52c18f824ae71
parentc2a22bd05e0c8282175422df26e188ef63bcc452 (diff)
parent5e051c08f69cb17d610c14b208c68fc4896c55ba (diff)
downloadservo-8cb05a36bc1a20ec6373ddb23ce127c7801ba5d6.tar.gz
servo-8cb05a36bc1a20ec6373ddb23ce127c7801ba5d6.zip
Auto merge of #12344 - izgzhen:set-file-js, r=Manishearth
Add ability to WPT-test file uploads and fetches, fixes #12322 Using `inputElem.selectFiles(["path1", "path2"])` in JavaScript with pref `dom.htmlinputelement.select_files.enabled` = `true` will simulate the effect of `inputElem.click()`. See #12322 for more, also related to #12343. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/12344) <!-- Reviewable:end -->
-rw-r--r--components/net/filemanager_thread.rs41
-rw-r--r--components/net_traits/filemanager_thread.rs4
-rw-r--r--components/script/dom/htmlinputelement.rs144
-rw-r--r--components/script/dom/webidls/HTMLInputElement.webidl5
-rw-r--r--tests/unit/net/filemanager_thread.rs4
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json6
-rw-r--r--tests/wpt/mozilla/meta/mozilla/file_upload.html.ini3
-rw-r--r--tests/wpt/mozilla/tests/mozilla/file_upload.html31
-rw-r--r--tests/wpt/mozilla/tests/mozilla/test.txt1
9 files changed, 168 insertions, 71 deletions
diff --git a/components/net/filemanager_thread.rs b/components/net/filemanager_thread.rs
index 3ff8579e0d2..627dcced0e9 100644
--- a/components/net/filemanager_thread.rs
+++ b/components/net/filemanager_thread.rs
@@ -21,6 +21,7 @@ use std::sync::{Arc, RwLock};
#[cfg(any(target_os = "macos", target_os = "linux"))]
use tinyfiledialogs;
use url::Url;
+use util::prefs::PREFS;
use util::thread::spawn_named;
use uuid::Uuid;
@@ -128,14 +129,14 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
loop {
let store = self.store.clone();
match self.receiver.recv().unwrap() {
- FileManagerThreadMsg::SelectFile(filter, sender, origin) => {
+ FileManagerThreadMsg::SelectFile(filter, sender, origin, opt_test_path) => {
spawn_named("select file".to_owned(), move || {
- store.select_file(filter, sender, origin);
+ store.select_file(filter, sender, origin, opt_test_path);
});
}
- FileManagerThreadMsg::SelectFiles(filter, sender, origin) => {
+ FileManagerThreadMsg::SelectFiles(filter, sender, origin, opt_test_paths) => {
spawn_named("select files".to_owned(), move || {
- store.select_files(filter, sender, origin);
+ store.select_files(filter, sender, origin, opt_test_paths);
})
}
FileManagerThreadMsg::ReadFile(sender, id, origin) => {
@@ -309,8 +310,17 @@ impl <UI: 'static + UIProvider> FileManagerStore<UI> {
fn select_file(&self, patterns: Vec<FilterPattern>,
sender: IpcSender<FileManagerResult<SelectedFile>>,
- origin: FileOrigin) {
- match self.ui.open_file_dialog("", patterns) {
+ origin: FileOrigin, opt_test_path: Option<String>) {
+ // Check if the select_files preference is enabled
+ // to ensure process-level security against compromised script;
+ // Then try applying opt_test_path directly for testing convenience
+ let opt_s = if select_files_pref_enabled() {
+ opt_test_path
+ } else {
+ self.ui.open_file_dialog("", patterns)
+ };
+
+ match opt_s {
Some(s) => {
let selected_path = Path::new(&s);
@@ -328,8 +338,17 @@ impl <UI: 'static + UIProvider> FileManagerStore<UI> {
fn select_files(&self, patterns: Vec<FilterPattern>,
sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>,
- origin: FileOrigin) {
- match self.ui.open_file_dialog_multi("", patterns) {
+ origin: FileOrigin, opt_test_paths: Option<Vec<String>>) {
+ // Check if the select_files preference is enabled
+ // to ensure process-level security against compromised script;
+ // Then try applying opt_test_paths directly for testing convenience
+ let opt_v = if select_files_pref_enabled() {
+ opt_test_paths
+ } else {
+ self.ui.open_file_dialog_multi("", patterns)
+ };
+
+ match opt_v {
Some(v) => {
let mut selected_paths = vec![];
@@ -481,3 +500,9 @@ impl <UI: 'static + UIProvider> FileManagerStore<UI> {
}
}
}
+
+
+fn select_files_pref_enabled() -> bool {
+ PREFS.get("dom.testing.htmlinputelement.select_files.enabled")
+ .as_boolean().unwrap_or(false)
+}
diff --git a/components/net_traits/filemanager_thread.rs b/components/net_traits/filemanager_thread.rs
index 78efc142a80..88f1b147302 100644
--- a/components/net_traits/filemanager_thread.rs
+++ b/components/net_traits/filemanager_thread.rs
@@ -118,10 +118,10 @@ pub struct FilterPattern(pub String);
#[derive(Deserialize, Serialize)]
pub enum FileManagerThreadMsg {
/// Select a single file, return triple (FileID, FileName, lastModified)
- SelectFile(Vec<FilterPattern>, IpcSender<FileManagerResult<SelectedFile>>, FileOrigin),
+ SelectFile(Vec<FilterPattern>, IpcSender<FileManagerResult<SelectedFile>>, FileOrigin, Option<String>),
/// Select multiple files, return a vector of triples
- SelectFiles(Vec<FilterPattern>, IpcSender<FileManagerResult<Vec<SelectedFile>>>, FileOrigin),
+ SelectFiles(Vec<FilterPattern>, IpcSender<FileManagerResult<Vec<SelectedFile>>>, FileOrigin, Option<Vec<String>>),
/// Read file, return the bytes
ReadFile(IpcSender<FileManagerResult<Vec<u8>>>, SelectedFileId, FileOrigin),
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 0ee2023e44a..d8acc2dbe75 100644
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -579,6 +579,16 @@ impl HTMLInputElementMethods for HTMLInputElement {
EventCancelable::NotCancelable);
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
}
+
+ // Select the files based on filepaths passed in,
+ // enabled by dom.htmlinputelement.select_files.enabled,
+ // used for test purpose.
+ // check-tidy: no specs after this line
+ fn SelectFiles(&self, paths: Vec<DOMString>) {
+ if self.input_type.get() == InputType::InputFile {
+ self.select_files(Some(paths));
+ }
+ }
}
@@ -731,6 +741,80 @@ impl HTMLInputElement {
let el = self.upcast::<Element>();
el.set_placeholder_shown_state(has_placeholder && !has_value);
}
+
+ // https://html.spec.whatwg.org/multipage/#file-upload-state-(type=file)
+ // Select files by invoking UI or by passed in argument
+ fn select_files(&self, opt_test_paths: Option<Vec<DOMString>>) {
+ let window = window_from_node(self);
+ let origin = window.get_url().origin().unicode_serialization();
+ let filemanager = window.resource_threads().sender();
+
+ let mut files: Vec<Root<File>> = vec![];
+ let mut error = None;
+
+ let filter = filter_from_accept(&self.Accept());
+ let target = self.upcast::<EventTarget>();
+
+ if self.Multiple() {
+ let opt_test_paths = opt_test_paths.map(|paths| paths.iter().map(|p| p.to_string()).collect());
+
+ let (chan, recv) = ipc::channel().expect("Error initializing channel");
+ let msg = FileManagerThreadMsg::SelectFiles(filter, chan, origin, opt_test_paths);
+ 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));
+ }
+
+ target.fire_event("input",
+ EventBubbles::Bubbles,
+ EventCancelable::NotCancelable);
+ target.fire_event("change",
+ EventBubbles::Bubbles,
+ EventCancelable::NotCancelable);
+ },
+ Err(err) => error = Some(err),
+ };
+ } else {
+ let opt_test_path = match opt_test_paths {
+ Some(paths) => {
+ if paths.len() == 0 {
+ return;
+ } else {
+ Some(paths[0].to_string()) // neglect other paths
+ }
+ }
+ None => None,
+ };
+
+ let (chan, recv) = ipc::channel().expect("Error initializing channel");
+ let msg = FileManagerThreadMsg::SelectFile(filter, chan, origin, opt_test_path);
+ let _ = filemanager.send(msg).unwrap();
+
+ match recv.recv().expect("IpcSender side error") {
+ Ok(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),
+ };
+ }
+
+ if let Some(err) = error {
+ debug!("Input file select error: {:?}", err);
+ } else {
+ let filelist = FileList::new(window.r(), files);
+ self.filelist.set(Some(&filelist));
+ }
+ }
}
impl VirtualMethods for HTMLInputElement {
@@ -1149,65 +1233,7 @@ impl Activatable for HTMLInputElement {
EventBubbles::Bubbles,
EventCancelable::NotCancelable);
},
- InputType::InputFile => {
- // https://html.spec.whatwg.org/multipage/#file-upload-state-(type=file)
- let window = window_from_node(self);
- let origin = window.get_url().origin().unicode_serialization();
- let filemanager = window.resource_threads().sender();
-
- let mut files: Vec<Root<File>> = vec![];
- let mut error = None;
-
- let filter = filter_from_accept(&self.Accept());
- let target = self.upcast::<EventTarget>();
-
- if self.Multiple() {
- let (chan, recv) = ipc::channel().expect("Error initializing channel");
- let msg = FileManagerThreadMsg::SelectFiles(filter, chan, origin);
- 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));
- }
-
- target.fire_event("input",
- EventBubbles::Bubbles,
- EventCancelable::NotCancelable);
- target.fire_event("change",
- EventBubbles::Bubbles,
- EventCancelable::NotCancelable);
- },
- Err(err) => error = Some(err),
- };
- } else {
- let (chan, recv) = ipc::channel().expect("Error initializing channel");
- let msg = FileManagerThreadMsg::SelectFile(filter, chan, origin);
- let _ = filemanager.send(msg).unwrap();
-
- match recv.recv().expect("IpcSender side error") {
- Ok(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),
- };
- }
-
- if let Some(err) = error {
- debug!("Input file select error: {:?}", err);
- } else {
- let filelist = FileList::new(window.r(), files);
- self.filelist.set(Some(&filelist));
- }
- }
+ InputType::InputFile => self.select_files(None),
_ => ()
}
}
diff --git a/components/script/dom/webidls/HTMLInputElement.webidl b/components/script/dom/webidls/HTMLInputElement.webidl
index d145e498659..1d6160b14cd 100644
--- a/components/script/dom/webidls/HTMLInputElement.webidl
+++ b/components/script/dom/webidls/HTMLInputElement.webidl
@@ -70,6 +70,11 @@ interface HTMLInputElement : HTMLElement {
void setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction);
// also has obsolete members
+
+ // Select with file-system paths for testing purpose
+ [Pref="dom.testing.htmlinputelement.select_files.enabled"]
+ void selectFiles(sequence<DOMString> path);
+
};
// https://html.spec.whatwg.org/multipage/#HTMLInputElement-partial
diff --git a/tests/unit/net/filemanager_thread.rs b/tests/unit/net/filemanager_thread.rs
index 27ff56d6a1e..036fdc969c7 100644
--- a/tests/unit/net/filemanager_thread.rs
+++ b/tests/unit/net/filemanager_thread.rs
@@ -40,7 +40,7 @@ fn test_filemanager() {
{
// Try to select a dummy file "tests/unit/net/test.txt"
let (tx, rx) = ipc::channel().unwrap();
- chan.send(FileManagerThreadMsg::SelectFile(patterns.clone(), tx, origin.clone())).unwrap();
+ chan.send(FileManagerThreadMsg::SelectFile(patterns.clone(), tx, origin.clone(), None)).unwrap();
let selected = rx.recv().expect("Broken channel")
.expect("The file manager failed to find test.txt");
@@ -88,7 +88,7 @@ fn test_filemanager() {
{
let (tx, rx) = ipc::channel().unwrap();
- let _ = chan.send(FileManagerThreadMsg::SelectFile(patterns.clone(), tx, origin.clone()));
+ let _ = chan.send(FileManagerThreadMsg::SelectFile(patterns.clone(), tx, origin.clone(), None));
assert!(rx.try_recv().is_err(), "The thread should not respond normally after exited");
}
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index b8611469cee..94676a48fdc 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -6456,6 +6456,12 @@
"url": "/_mozilla/mozilla/event_listener.html"
}
],
+ "mozilla/file_upload.html": [
+ {
+ "path": "mozilla/file_upload.html",
+ "url": "/_mozilla/mozilla/file_upload.html"
+ }
+ ],
"mozilla/focus_blur.html": [
{
"path": "mozilla/focus_blur.html",
diff --git a/tests/wpt/mozilla/meta/mozilla/file_upload.html.ini b/tests/wpt/mozilla/meta/mozilla/file_upload.html.ini
new file mode 100644
index 00000000000..8c9a7d45d7d
--- /dev/null
+++ b/tests/wpt/mozilla/meta/mozilla/file_upload.html.ini
@@ -0,0 +1,3 @@
+[file_upload.html]
+ type: testharness
+ prefs: [dom.testing.htmlinputelement.select_files.enabled:true]
diff --git a/tests/wpt/mozilla/tests/mozilla/file_upload.html b/tests/wpt/mozilla/tests/mozilla/file_upload.html
new file mode 100644
index 00000000000..ad911097f6c
--- /dev/null
+++ b/tests/wpt/mozilla/tests/mozilla/file_upload.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test of uploading a file through input element</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body><input id="file-input" type="file"></body>
+
+<script>
+
+async_test(function() {
+ var e = document.getElementById("file-input");
+
+ assert_true(e.selectFiles != undefined, "selectFiles is not defined")
+
+ e.selectFiles(["./tests/wpt/mozilla/tests/mozilla/test.txt"]);
+
+ assert_true(e.files.length > 0, "test.txt is not selected");
+
+ var reader = new FileReader;
+
+ reader.onloadend = this.step_func(function(evt) {
+ assert_equals(evt.target.result, "hello, servo\n");
+
+ this.done();
+ });
+
+ reader.readAsText(e.files[0]);
+
+}, "Select a file");
+</script>
diff --git a/tests/wpt/mozilla/tests/mozilla/test.txt b/tests/wpt/mozilla/tests/mozilla/test.txt
new file mode 100644
index 00000000000..9235007d960
--- /dev/null
+++ b/tests/wpt/mozilla/tests/mozilla/test.txt
@@ -0,0 +1 @@
+hello, servo