aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZhen Zhang <izgzhen@gmail.com>2016-07-21 08:31:25 +0200
committerZhen Zhang <izgzhen@gmail.com>2016-08-02 09:05:42 +0200
commitef091179f552ac800c62d1e08ca5f8934c5eb06e (patch)
tree5aa96bb5168097fcf57517d35d6eb3d8d9432cce
parent2553bb7af4f3f5cd0b3df5431a21e080e42f5f72 (diff)
downloadservo-ef091179f552ac800c62d1e08ca5f8934c5eb06e.tar.gz
servo-ef091179f552ac800c62d1e08ca5f8934c5eb06e.zip
Add form submission for file type input and related fixings
-rw-r--r--components/script/dom/filelist.rs5
-rw-r--r--components/script/dom/htmlformelement.rs190
-rw-r--r--components/script/dom/htmlinputelement.rs43
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json46
-rw-r--r--tests/wpt/mozilla/meta/mozilla/FileAPI/blob_url_upload.html.ini (renamed from tests/wpt/mozilla/meta/mozilla/blob_url_upload.html.ini)0
-rw-r--r--tests/wpt/mozilla/meta/mozilla/FileAPI/file-select.html.ini (renamed from tests/wpt/mozilla/meta/mozilla/file_upload.html.ini)2
-rw-r--r--tests/wpt/mozilla/meta/mozilla/FileAPI/file-upload.html.ini3
-rw-r--r--tests/wpt/mozilla/tests/mozilla/FileAPI/blob.html (renamed from tests/wpt/mozilla/tests/mozilla/blob.html)0
-rw-r--r--tests/wpt/mozilla/tests/mozilla/FileAPI/blob_url_upload.html (renamed from tests/wpt/mozilla/tests/mozilla/blob_url_upload.html)0
-rw-r--r--tests/wpt/mozilla/tests/mozilla/FileAPI/blob_url_upload_ref.html (renamed from tests/wpt/mozilla/tests/mozilla/blob_url_upload_ref.html)2
-rw-r--r--tests/wpt/mozilla/tests/mozilla/FileAPI/file-select.html (renamed from tests/wpt/mozilla/tests/mozilla/file_upload.html)2
-rw-r--r--tests/wpt/mozilla/tests/mozilla/FileAPI/file-upload-frame.html8
-rw-r--r--tests/wpt/mozilla/tests/mozilla/FileAPI/file-upload.html23
-rw-r--r--tests/wpt/mozilla/tests/mozilla/FileAPI/resource/file-submission.py29
-rw-r--r--tests/wpt/mozilla/tests/mozilla/FileAPI/resource/upload.txt1
15 files changed, 242 insertions, 112 deletions
diff --git a/components/script/dom/filelist.rs b/components/script/dom/filelist.rs
index 96bfc4ce0d3..4f8e976f5f7 100644
--- a/components/script/dom/filelist.rs
+++ b/components/script/dom/filelist.rs
@@ -9,6 +9,7 @@ use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::file::File;
use dom::window::Window;
+use std::slice::Iter;
// https://w3c.github.io/FileAPI/#dfn-filelist
#[dom_struct]
@@ -32,6 +33,10 @@ impl FileList {
GlobalRef::Window(window),
FileListBinding::Wrap)
}
+
+ pub fn iter_files(&self) -> Iter<JS<File>> {
+ self.list.iter()
+ }
}
impl FileListMethods for FileList {
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index 9d7ce208ee2..72b8cc19c16 100644
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -37,11 +37,9 @@ use dom::htmlselectelement::HTMLSelectElement;
use dom::htmltextareaelement::HTMLTextAreaElement;
use dom::node::{Node, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
-use dom::window::Window;
use encoding::EncodingRef;
use encoding::all::UTF_8;
use encoding::label::encoding_from_whatwg_label;
-use encoding::types::DecoderTrap;
use hyper::header::{Charset, ContentDisposition, ContentType, DispositionParam, DispositionType};
use hyper::method::Method;
use msg::constellation_msg::{LoadData, PipelineId};
@@ -54,7 +52,6 @@ use string_cache::Atom;
use style::attr::AttrValue;
use style::str::split_html_space_chars;
use task_source::TaskSource;
-use url::form_urlencoded;
#[derive(JSTraceable, PartialEq, Clone, Copy, HeapSizeOf)]
pub struct GenerationId(u32);
@@ -280,39 +277,38 @@ impl HTMLFormElement {
// https://html.spec.whatwg.org/multipage/#multipart/form-data-encoding-algorithm
fn encode_multipart_form_data(&self, form_data: &mut Vec<FormDatum>,
- encoding: Option<EncodingRef>,
- boundary: String) -> String {
+ boundary: String, encoding: EncodingRef) -> Vec<u8> {
// Step 1
- let mut result = "".to_owned();
+ let mut result = vec![];
// Step 2
- // (maybe take encoding as input)
- let encoding = encoding.unwrap_or(self.pick_encoding());
-
- // Step 3
let charset = &*encoding.whatwg_name().unwrap_or("UTF-8");
- // Step 4
+ // Step 3
for entry in form_data.iter_mut() {
- // Substep 1
+ // 3.1
if entry.name == "_charset_" && entry.ty == "hidden" {
entry.value = FormDatumValue::String(DOMString::from(charset.clone()));
}
- // TODO: Substep 2
+ // TODO: 3.2
- // Step 5
+ // Step 4
// https://tools.ietf.org/html/rfc7578#section-4
- result.push_str(&*format!("\r\n--{}\r\n", boundary));
+ // NOTE(izgzhen): The encoding here expected by most servers seems different from
+ // what spec says (that it should start with a '\r\n').
+ let mut boundary_bytes = format!("--{}\r\n", boundary).into_bytes();
+ result.append(&mut boundary_bytes);
let mut content_disposition = ContentDisposition {
disposition: DispositionType::Ext("form-data".to_owned()),
parameters: vec![DispositionParam::Ext("name".to_owned(), String::from(entry.name.clone()))]
};
match entry.value {
- FormDatumValue::String(ref s) =>
- result.push_str(&*format!("Content-Disposition: {}\r\n\r\n{}",
- content_disposition,
- s)),
+ FormDatumValue::String(ref s) => {
+ let mut bytes = format!("Content-Disposition: {}\r\n\r\n{}",
+ content_disposition, s).into_bytes();
+ result.append(&mut bytes);
+ }
FormDatumValue::File(ref f) => {
content_disposition.parameters.push(
DispositionParam::Filename(Charset::Ext(String::from(charset.clone())),
@@ -321,20 +317,20 @@ impl HTMLFormElement {
// https://tools.ietf.org/html/rfc7578#section-4.4
let content_type = ContentType(f.upcast::<Blob>().Type()
.parse().unwrap_or(mime!(Text / Plain)));
- result.push_str(&*format!("Content-Disposition: {}\r\n{}\r\n\r\n",
- content_disposition,
- content_type));
+ let mut type_bytes = format!("Content-Disposition: {}\r\n{}\r\n\r\n",
+ content_disposition,
+ content_type).into_bytes();
+ result.append(&mut type_bytes);
- let bytes = &f.upcast::<Blob>().get_bytes().unwrap_or(vec![])[..];
+ let mut bytes = f.upcast::<Blob>().get_bytes().unwrap_or(vec![]);
- let decoded = encoding.decode(bytes, DecoderTrap::Replace)
- .expect("Invalid encoding in file");
- result.push_str(&decoded);
+ result.append(&mut bytes);
}
}
}
- result.push_str(&*format!("\r\n--{}--", boundary));
+ let mut boundary_bytes = format!("\r\n--{}--", boundary).into_bytes();
+ result.append(&mut boundary_bytes);
result
}
@@ -351,18 +347,11 @@ impl HTMLFormElement {
let charset = &*encoding.whatwg_name().unwrap();
for entry in form_data.iter_mut() {
- // Step 4
- if entry.name == "_charset_" && entry.ty == "hidden" {
- entry.value = FormDatumValue::String(DOMString::from(charset.clone()));
- }
-
- // Step 5
- if entry.ty == "file" {
- entry.value = FormDatumValue::String(DOMString::from(entry.value_str()));
- }
+ // Step 4, 5
+ let value = entry.replace_value(charset);
// Step 6
- result.push_str(&*format!("{}={}\r\n", entry.name, entry.value_str()));
+ result.push_str(&*format!("{}={}\r\n", entry.name, value));
}
// Step 7
@@ -374,7 +363,7 @@ impl HTMLFormElement {
// Step 1
let doc = document_from_node(self);
let base = doc.url();
- // TODO: Handle browsing contexts
+ // TODO: Handle browsing contexts (Step 2, 3)
// Step 4
if submit_method_flag == SubmittedFrom::NotFromForm &&
!submitter.no_validate(self)
@@ -397,13 +386,18 @@ impl HTMLFormElement {
}
// Step 6
let mut form_data = self.get_form_dataset(Some(submitter));
+
// Step 7
- let mut action = submitter.action();
+ let encoding = self.pick_encoding();
+
// Step 8
+ let mut action = submitter.action();
+
+ // Step 9
if action.is_empty() {
action = DOMString::from(base.as_str());
}
- // Step 9-11
+ // Step 10-11
let action_components = match base.join(&action) {
Ok(url) => url,
Err(_) => return
@@ -417,57 +411,87 @@ impl HTMLFormElement {
let mut load_data = LoadData::new(action_components, doc.get_referrer_policy(), Some(doc.url().clone()));
- let parsed_data = match enctype {
+ // Step 18
+ match (&*scheme, method) {
+ (_, FormMethod::FormDialog) => {
+ // TODO: Submit dialog
+ // https://html.spec.whatwg.org/multipage/#submit-dialog
+ }
+ // https://html.spec.whatwg.org/multipage/#submit-mutate-action
+ ("http", FormMethod::FormGet) | ("https", FormMethod::FormGet) | ("data", FormMethod::FormGet) => {
+ load_data.headers.set(ContentType::form_url_encoded());
+ self.mutate_action_url(&mut form_data, load_data, encoding);
+ }
+ // https://html.spec.whatwg.org/multipage/#submit-body
+ ("http", FormMethod::FormPost) | ("https", FormMethod::FormPost) => {
+ load_data.method = Method::Post;
+ self.submit_entity_body(&mut form_data, load_data, enctype, encoding);
+ }
+ // https://html.spec.whatwg.org/multipage/#submit-get-action
+ ("file", _) | ("about", _) | ("data", FormMethod::FormPost) |
+ ("ftp", _) | ("javascript", _) => {
+ self.plan_to_navigate(load_data);
+ }
+ ("mailto", FormMethod::FormPost) => {
+ // TODO: Mail as body
+ // https://html.spec.whatwg.org/multipage/#submit-mailto-body
+ }
+ ("mailto", FormMethod::FormGet) => {
+ // TODO: Mail with headers
+ // https://html.spec.whatwg.org/multipage/#submit-mailto-headers
+ }
+ _ => return,
+ }
+ }
+
+ // https://html.spec.whatwg.org/multipage/#submit-mutate-action
+ fn mutate_action_url(&self, form_data: &mut Vec<FormDatum>, mut load_data: LoadData, encoding: EncodingRef) {
+ let charset = &*encoding.whatwg_name().unwrap();
+
+ load_data.url.query_pairs_mut().clear()
+ .encoding_override(Some(self.pick_encoding()))
+ .extend_pairs(form_data.into_iter()
+ .map(|field| (field.name.clone(), field.replace_value(charset))));
+
+ self.plan_to_navigate(load_data);
+ }
+
+ // https://html.spec.whatwg.org/multipage/#submit-body
+ fn submit_entity_body(&self, form_data: &mut Vec<FormDatum>, mut load_data: LoadData,
+ enctype: FormEncType, encoding: EncodingRef) {
+ let boundary = self.generate_boundary();
+ let bytes = match enctype {
FormEncType::UrlEncoded => {
+ let mut url = load_data.url.clone();
+ let charset = &*encoding.whatwg_name().unwrap();
load_data.headers.set(ContentType::form_url_encoded());
- form_urlencoded::Serializer::new(String::new())
- .encoding_override(Some(self.pick_encoding()))
- .extend_pairs(form_data.into_iter().map(|field| (field.name.clone(), field.value_str())))
- .finish()
+ url.query_pairs_mut().clear()
+ .encoding_override(Some(self.pick_encoding()))
+ .extend_pairs(form_data.into_iter()
+ .map(|field| (field.name.clone(), field.replace_value(charset))));
+
+ url.query().unwrap_or("").to_string().into_bytes()
}
FormEncType::FormDataEncoded => {
- let boundary = self.generate_boundary();
let mime = mime!(Multipart / FormData; Boundary =(&boundary));
load_data.headers.set(ContentType(mime));
-
- self.encode_multipart_form_data(&mut form_data, None, boundary)
+ self.encode_multipart_form_data(form_data, boundary, encoding)
}
FormEncType::TextPlainEncoded => {
load_data.headers.set(ContentType(mime!(Text / Plain)));
-
- self.encode_plaintext(&mut form_data)
+ self.encode_plaintext(form_data).into_bytes()
}
};
- // Step 18
- let win = window_from_node(self);
- match (&*scheme, method) {
- // https://html.spec.whatwg.org/multipage/#submit-dialog
- (_, FormMethod::FormDialog) => return, // Unimplemented
- // https://html.spec.whatwg.org/multipage/#submit-mutate-action
- ("http", FormMethod::FormGet) | ("https", FormMethod::FormGet) => {
- // FIXME(SimonSapin): use url.query_pairs_mut() here.
- load_data.url.set_query(Some(&*parsed_data));
- self.plan_to_navigate(load_data, &win);
- }
- // https://html.spec.whatwg.org/multipage/#submit-body
- ("http", FormMethod::FormPost) | ("https", FormMethod::FormPost) => {
- load_data.method = Method::Post;
- load_data.data = Some(parsed_data.into_bytes());
- self.plan_to_navigate(load_data, &win);
- }
- // https://html.spec.whatwg.org/multipage/#submit-get-action
- ("file", _) | ("about", _) | ("data", FormMethod::FormGet) |
- ("ftp", _) | ("javascript", _) => {
- self.plan_to_navigate(load_data, &win);
- }
- _ => return // Unimplemented (data and mailto)
- }
+ load_data.data = Some(bytes);
+ self.plan_to_navigate(load_data);
}
/// [Planned navigation](https://html.spec.whatwg.org/multipage/#planned-navigation)
- fn plan_to_navigate(&self, load_data: LoadData, window: &Window) {
+ fn plan_to_navigate(&self, load_data: LoadData) {
+ let window = window_from_node(self);
+
// Step 1
// Each planned navigation runnable is tagged with a generation ID, and
// before the runnable is handled, it first checks whether the HTMLFormElement's
@@ -485,7 +509,7 @@ impl HTMLFormElement {
};
// Step 3
- window.dom_manipulation_task_source().queue(nav, GlobalRef::Window(window)).unwrap();
+ window.dom_manipulation_task_source().queue(nav, GlobalRef::Window(&window)).unwrap();
}
/// Interactively validate the constraints of form elements
@@ -558,10 +582,8 @@ impl HTMLFormElement {
match element {
HTMLElementTypeId::HTMLInputElement => {
let input = child.downcast::<HTMLInputElement>().unwrap();
- // Step 3.2-3.7
- if let Some(datum) = input.form_datum(submitter) {
- data_set.push(datum);
- }
+
+ data_set.append(&mut input.form_datums(submitter));
}
HTMLElementTypeId::HTMLButtonElement => {
let button = child.downcast::<HTMLButtonElement>().unwrap();
@@ -709,10 +731,14 @@ pub struct FormDatum {
}
impl FormDatum {
- pub fn value_str(&self) -> String {
+ pub fn replace_value(&self, charset: &str) -> String {
+ if self.name == "_charset_" && self.ty == "hidden" {
+ return charset.to_string();
+ }
+
match self.value {
+ FormDatumValue::File(ref f) => String::from(f.name().clone()),
FormDatumValue::String(ref s) => String::from(s.clone()),
- FormDatumValue::File(ref f) => String::from(f.name().clone())
}
}
}
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 7f524cac29d..6f7a7a755f5 100644
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -648,7 +648,7 @@ impl HTMLInputElement {
/// https://html.spec.whatwg.org/multipage/#constructing-the-form-data-set
/// Steps range from 3.1 to 3.7 (specific to HTMLInputElement)
- pub fn form_datum(&self, submitter: Option<FormSubmitter>) -> Option<FormDatum> {
+ pub fn form_datums(&self, submitter: Option<FormSubmitter>) -> Vec<FormDatum> {
// 3.1: disabled state check is in get_unclean_dataset
// Step 3.2
@@ -664,26 +664,55 @@ impl HTMLInputElement {
match ty {
// Step 3.1: it's a button but it is not submitter.
- atom!("submit") | atom!("button") | atom!("reset") if !is_submitter => return None,
+ atom!("submit") | atom!("button") | atom!("reset") if !is_submitter => return vec![],
// Step 3.1: it's the "Checkbox" or "Radio Button" and whose checkedness is false.
atom!("radio") | atom!("checkbox") => if !self.Checked() || name.is_empty() {
- return None;
+ return vec![];
},
+ atom!("file") => {
+ let mut datums = vec![];
+
+ // Step 3.2-3.7
+ let name = self.Name();
+ let type_ = self.Type();
+
+ match self.GetFiles() {
+ Some(fl) => {
+ for f in fl.iter_files() {
+ datums.push(FormDatum {
+ ty: type_.clone(),
+ name: name.clone(),
+ value: FormDatumValue::File(Root::from_ref(&f)),
+ });
+ }
+ }
+ None => {
+ datums.push(FormDatum {
+ // XXX(izgzhen): Spec says 'application/octet-stream' as the type,
+ // but this is _type_ of element rather than content right?
+ ty: type_.clone(),
+ name: name.clone(),
+ value: FormDatumValue::String(DOMString::from("")),
+ })
+ }
+ }
- atom!("image") | atom!("file") => return None, // Unimplemented
+ return datums;
+ }
+ atom!("image") => return vec![], // Unimplemented
// Step 3.1: it's not the "Image Button" and doesn't have a name attribute.
_ => if name.is_empty() {
- return None;
+ return vec![];
}
}
// Step 3.9
- Some(FormDatum {
+ vec![FormDatum {
ty: DOMString::from(&*ty), // FIXME(ajeffrey): Convert directly from Atoms to DOMStrings
name: name,
value: FormDatumValue::String(self.Value())
- })
+ }]
}
// https://html.spec.whatwg.org/multipage/#radio-button-group
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index 6fd9384d01e..20107d1e105 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -5806,16 +5806,16 @@
"url": "/_mozilla/css/word_break_a.html"
}
],
- "mozilla/blob_url_upload.html": [
+ "mozilla/FileAPI/blob_url_upload.html": [
{
- "path": "mozilla/blob_url_upload.html",
+ "path": "mozilla/FileAPI/blob_url_upload.html",
"references": [
[
- "/_mozilla/mozilla/blob_url_upload_ref.html",
+ "/_mozilla/mozilla/FileAPI/blob_url_upload_ref.html",
"=="
]
],
- "url": "/_mozilla/mozilla/blob_url_upload.html"
+ "url": "/_mozilla/mozilla/FileAPI/blob_url_upload.html"
}
],
"mozilla/canvas/drawimage_html_image_1.html": [
@@ -6234,6 +6234,24 @@
"url": "/_mozilla/mozilla/Event.html"
}
],
+ "mozilla/FileAPI/blob.html": [
+ {
+ "path": "mozilla/FileAPI/blob.html",
+ "url": "/_mozilla/mozilla/FileAPI/blob.html"
+ }
+ ],
+ "mozilla/FileAPI/file-select.html": [
+ {
+ "path": "mozilla/FileAPI/file-select.html",
+ "url": "/_mozilla/mozilla/FileAPI/file-select.html"
+ }
+ ],
+ "mozilla/FileAPI/file-upload.html": [
+ {
+ "path": "mozilla/FileAPI/file-upload.html",
+ "url": "/_mozilla/mozilla/FileAPI/file-upload.html"
+ }
+ ],
"mozilla/FocusEvent.html": [
{
"path": "mozilla/FocusEvent.html",
@@ -6258,12 +6276,6 @@
"url": "/_mozilla/mozilla/binding_keyword.html"
}
],
- "mozilla/blob.html": [
- {
- "path": "mozilla/blob.html",
- "url": "/_mozilla/mozilla/blob.html"
- }
- ],
"mozilla/body_listener.html": [
{
"path": "mozilla/body_listener.html",
@@ -6540,12 +6552,6 @@
"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",
@@ -14936,16 +14942,16 @@
"url": "/_mozilla/css/word_break_a.html"
}
],
- "mozilla/blob_url_upload.html": [
+ "mozilla/FileAPI/blob_url_upload.html": [
{
- "path": "mozilla/blob_url_upload.html",
+ "path": "mozilla/FileAPI/blob_url_upload.html",
"references": [
[
- "/_mozilla/mozilla/blob_url_upload_ref.html",
+ "/_mozilla/mozilla/FileAPI/blob_url_upload_ref.html",
"=="
]
],
- "url": "/_mozilla/mozilla/blob_url_upload.html"
+ "url": "/_mozilla/mozilla/FileAPI/blob_url_upload.html"
}
],
"mozilla/canvas/drawimage_html_image_1.html": [
diff --git a/tests/wpt/mozilla/meta/mozilla/blob_url_upload.html.ini b/tests/wpt/mozilla/meta/mozilla/FileAPI/blob_url_upload.html.ini
index 6ad257ede65..6ad257ede65 100644
--- a/tests/wpt/mozilla/meta/mozilla/blob_url_upload.html.ini
+++ b/tests/wpt/mozilla/meta/mozilla/FileAPI/blob_url_upload.html.ini
diff --git a/tests/wpt/mozilla/meta/mozilla/file_upload.html.ini b/tests/wpt/mozilla/meta/mozilla/FileAPI/file-select.html.ini
index 8c9a7d45d7d..b047e230d99 100644
--- a/tests/wpt/mozilla/meta/mozilla/file_upload.html.ini
+++ b/tests/wpt/mozilla/meta/mozilla/FileAPI/file-select.html.ini
@@ -1,3 +1,3 @@
-[file_upload.html]
+[file-select.html]
type: testharness
prefs: [dom.testing.htmlinputelement.select_files.enabled:true]
diff --git a/tests/wpt/mozilla/meta/mozilla/FileAPI/file-upload.html.ini b/tests/wpt/mozilla/meta/mozilla/FileAPI/file-upload.html.ini
new file mode 100644
index 00000000000..f3715f47bf9
--- /dev/null
+++ b/tests/wpt/mozilla/meta/mozilla/FileAPI/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/blob.html b/tests/wpt/mozilla/tests/mozilla/FileAPI/blob.html
index 3f932083542..3f932083542 100644
--- a/tests/wpt/mozilla/tests/mozilla/blob.html
+++ b/tests/wpt/mozilla/tests/mozilla/FileAPI/blob.html
diff --git a/tests/wpt/mozilla/tests/mozilla/blob_url_upload.html b/tests/wpt/mozilla/tests/mozilla/FileAPI/blob_url_upload.html
index 17c8e3ad0b4..17c8e3ad0b4 100644
--- a/tests/wpt/mozilla/tests/mozilla/blob_url_upload.html
+++ b/tests/wpt/mozilla/tests/mozilla/FileAPI/blob_url_upload.html
diff --git a/tests/wpt/mozilla/tests/mozilla/blob_url_upload_ref.html b/tests/wpt/mozilla/tests/mozilla/FileAPI/blob_url_upload_ref.html
index 2ae08600b45..6f95c43ac32 100644
--- a/tests/wpt/mozilla/tests/mozilla/blob_url_upload_ref.html
+++ b/tests/wpt/mozilla/tests/mozilla/FileAPI/blob_url_upload_ref.html
@@ -2,6 +2,6 @@
<meta charset="utf-8">
<title>Reference: Blob URL with File Upload</title>
<body>
- <img src="test.jpg" id="image">
+ <img src="../test.jpg" id="image">
<input type="file" id="file-input"">
</body>
diff --git a/tests/wpt/mozilla/tests/mozilla/file_upload.html b/tests/wpt/mozilla/tests/mozilla/FileAPI/file-select.html
index ad911097f6c..06a5f30dd44 100644
--- a/tests/wpt/mozilla/tests/mozilla/file_upload.html
+++ b/tests/wpt/mozilla/tests/mozilla/FileAPI/file-select.html
@@ -1,6 +1,6 @@
<!doctype html>
<meta charset="utf-8">
-<title>Test of uploading a file through input element</title>
+<title>Test of selecting a file through input element</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
diff --git a/tests/wpt/mozilla/tests/mozilla/FileAPI/file-upload-frame.html b/tests/wpt/mozilla/tests/mozilla/FileAPI/file-upload-frame.html
new file mode 100644
index 00000000000..13951bb37d0
--- /dev/null
+++ b/tests/wpt/mozilla/tests/mozilla/FileAPI/file-upload-frame.html
@@ -0,0 +1,8 @@
+<form id="testform" enctype="multipart/form-data" action="resource/file-submission.py" method="post">
+<input type="file" name="file-input" id="file-input" />
+</form>
+
+<script type="text/javascript">
+ var e = document.getElementById("file-input");
+ e.selectFiles(["./tests/wpt/mozilla/tests/mozilla/FileAPI/resource/upload.txt"]);
+</script>
diff --git a/tests/wpt/mozilla/tests/mozilla/FileAPI/file-upload.html b/tests/wpt/mozilla/tests/mozilla/FileAPI/file-upload.html
new file mode 100644
index 00000000000..bff5fb1ee7a
--- /dev/null
+++ b/tests/wpt/mozilla/tests/mozilla/FileAPI/file-upload.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+function run_test() {
+ var t = async_test("form submission of uploaded file");
+
+ var testframe = document.getElementById("testframe");
+ var testdocument = testframe.contentWindow.document;
+
+ testframe.onload = function() {
+ t.step(function () {
+ var response = testframe.contentDocument.documentElement.textContent;
+ assert_equals(response, "OK");
+ });
+ t.done();
+ };
+ testdocument.getElementById("testform").submit();
+}
+</script>
+<iframe id=testframe src="file-upload-frame.html" onload="run_test();"></iframe>
diff --git a/tests/wpt/mozilla/tests/mozilla/FileAPI/resource/file-submission.py b/tests/wpt/mozilla/tests/mozilla/FileAPI/resource/file-submission.py
new file mode 100644
index 00000000000..aa278cb4c4d
--- /dev/null
+++ b/tests/wpt/mozilla/tests/mozilla/FileAPI/resource/file-submission.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+def fail(msg):
+ return ([("Content-Type", "text/plain")], "FAIL: " + msg)
+
+
+def main(request, response):
+ content_type = request.headers.get('Content-Type').split("; ")
+
+ if len(content_type) != 2:
+ return fail("content type length is incorrect")
+
+ if content_type[0] != 'multipart/form-data':
+ return fail("content type first field is incorrect")
+
+ boundary = content_type[1].strip("boundary=")
+
+ body = "--" + boundary + "\r\nContent-Disposition: form-data; name=\"file-input\"; filename=\"upload.txt\""
+ body += "\r\n" + "text/plain\r\n\r\nHello\r\n--" + boundary + "--"
+
+ if body != request.body:
+ return fail("request body doesn't match: " + body + "+++++++" + request.body)
+
+ return ([("Content-Type", "text/plain")], "OK")
diff --git a/tests/wpt/mozilla/tests/mozilla/FileAPI/resource/upload.txt b/tests/wpt/mozilla/tests/mozilla/FileAPI/resource/upload.txt
new file mode 100644
index 00000000000..5ab2f8a4323
--- /dev/null
+++ b/tests/wpt/mozilla/tests/mozilla/FileAPI/resource/upload.txt
@@ -0,0 +1 @@
+Hello \ No newline at end of file