Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to WPT-test file uploads and fetches, fixes #12322 #12344

Merged
merged 1 commit into from Jul 9, 2016
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

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

This comment has been minimized.

@izgzhen

izgzhen Jul 9, 2016

Author Contributor

@Manishearth Looks good?

This comment has been minimized.

@Manishearth

Manishearth Jul 9, 2016

Member

Yep. Leave a comment here though mentioning that while this pref check is redundant, it's in place to ensure that a compromised script thread cannot request arbitrary files. r=me


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)
}
@@ -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),
@@ -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),
_ => ()
}
}
@@ -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
@@ -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");
}
@@ -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",
@@ -0,0 +1,3 @@
[file_upload.html]
type: testharness
prefs: [dom.testing.htmlinputelement.select_files.enabled:true]
@@ -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>
@@ -0,0 +1 @@
hello, servo
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.