diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2016-07-09 02:02:34 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-07-09 02:02:34 -0700 |
commit | 8cb05a36bc1a20ec6373ddb23ce127c7801ba5d6 (patch) | |
tree | 85a294c6d17493671656f7f32db52c18f824ae71 | |
parent | c2a22bd05e0c8282175422df26e188ef63bcc452 (diff) | |
parent | 5e051c08f69cb17d610c14b208c68fc4896c55ba (diff) | |
download | servo-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.rs | 41 | ||||
-rw-r--r-- | components/net_traits/filemanager_thread.rs | 4 | ||||
-rw-r--r-- | components/script/dom/htmlinputelement.rs | 144 | ||||
-rw-r--r-- | components/script/dom/webidls/HTMLInputElement.webidl | 5 | ||||
-rw-r--r-- | tests/unit/net/filemanager_thread.rs | 4 | ||||
-rw-r--r-- | tests/wpt/mozilla/meta/MANIFEST.json | 6 | ||||
-rw-r--r-- | tests/wpt/mozilla/meta/mozilla/file_upload.html.ini | 3 | ||||
-rw-r--r-- | tests/wpt/mozilla/tests/mozilla/file_upload.html | 31 | ||||
-rw-r--r-- | tests/wpt/mozilla/tests/mozilla/test.txt | 1 |
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 |