diff options
-rw-r--r-- | components/script/Cargo.toml | 1 | ||||
-rw-r--r-- | components/script/dom/htmlformelement.rs | 137 | ||||
-rw-r--r-- | components/script/dom/htmlinputelement.rs | 5 | ||||
-rw-r--r-- | components/script/dom/htmlselectelement.rs | 4 | ||||
-rw-r--r-- | components/script/lib.rs | 2 | ||||
-rw-r--r-- | components/servo/Cargo.lock | 1 | ||||
-rw-r--r-- | ports/cef/Cargo.lock | 1 | ||||
-rw-r--r-- | ports/gonk/Cargo.lock | 1 |
8 files changed, 135 insertions, 17 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index fe204e17bce..d032bc81921 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -44,6 +44,7 @@ hyper = { version = "0.9", features = [ "serde-serialization" ] } image = "0.9" libc = "0.2" log = "0.3.5" +mime = "0.2.0" num-traits = "0.1.32" offscreen_gl_context = "0.1.2" rand = "0.3" diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index 999ccacc8a8..cceb4160838 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::attr::AttrValue; +use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods; @@ -15,10 +16,12 @@ use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, Nod use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::Reflectable; +use dom::blob::Blob; use dom::document::Document; use dom::element::Element; use dom::event::{EventBubbles, EventCancelable}; use dom::eventtarget::EventTarget; +use dom::file::File; use dom::htmlbuttonelement::HTMLButtonElement; use dom::htmlcollection::CollectionFilter; use dom::htmldatalistelement::HTMLDataListElement; @@ -33,19 +36,23 @@ use dom::htmltextareaelement::HTMLTextAreaElement; use dom::node::{Node, document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; use dom::window::Window; -use hyper::header::ContentType; +use encoding::EncodingRef; +use encoding::all::UTF_8; +use encoding::label::encoding_from_whatwg_label; +use hyper::header::{Charset, ContentDisposition, ContentType, DispositionParam, DispositionType}; use hyper::method::Method; -use hyper::mime; use msg::constellation_msg::{LoadData, PipelineId}; +use rand::random; use script_runtime::ScriptChan; use script_thread::{MainThreadScriptMsg, Runnable}; use std::borrow::ToOwned; use std::cell::Cell; +use std::str::from_utf8; use std::sync::mpsc::Sender; use string_cache::Atom; use task_source::dom_manipulation::DOMManipulationTask; use url::form_urlencoded; -use util::str::DOMString; +use util::str::{DOMString, split_html_space_chars}; #[derive(JSTraceable, PartialEq, Clone, Copy, HeapSizeOf)] pub struct GenerationId(u32); @@ -237,6 +244,86 @@ pub enum ResetFrom { impl HTMLFormElement { + fn generate_boundary(&self) -> String { + let i1 = random::<u32>(); + let i2 = random::<u32>(); + + format!("---------------------------{0}{1}", i1, i2) + } + + // https://html.spec.whatwg.org/multipage/#picking-an-encoding-for-the-form + fn pick_encoding(&self) -> EncodingRef { + // Step 2 + if self.upcast::<Element>().has_attribute(&atom!("accept-charset")) { + // Substep 1 + let input = self.upcast::<Element>().get_string_attribute(&atom!("accept-charset")); + + // Substep 2, 3, 4 + let mut candidate_encodings = split_html_space_chars(&*input).filter_map(encoding_from_whatwg_label); + + // Substep 5, 6 + return candidate_encodings.next().unwrap_or(UTF_8); + } + + // Step 1, 3 + document_from_node(self).encoding() + } + + // https://html.spec.whatwg.org/multipage/#multipart/form-data-encoding-algorithm + fn encode_form_data(&self, form_data: &mut Vec<FormDatum>, + encoding: Option<EncodingRef>, + boundary: String) -> String { + // Step 1 + let mut result = "".to_owned(); + + // Step 2 + // (maybe take encoding as input) + let encoding = encoding.unwrap_or(self.pick_encoding()); + + // Step 3 + let charset = &*encoding.whatwg_name().unwrap(); + + // Step 4 + for entry in form_data.iter_mut() { + // Substep 1 + if entry.name == "_charset_" && entry.ty == "hidden" { + entry.value = FormDatumValue::String(DOMString::from(charset.clone())); + } + // TODO: Substep 2 + + // Step 5 + // https://tools.ietf.org/html/rfc7578#section-4 + result.push_str(&*format!("\r\n--{}\r\n", boundary)); + 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::File(ref f) => { + content_disposition.parameters.push( + DispositionParam::Filename(Charset::Ext(String::from(charset.clone())), + None, + f.name().clone().into())); + let content_type = ContentType(f.upcast::<Blob>().Type().parse().unwrap()); + result.push_str(&*format!("Content-Disposition: {}\r\n{}\r\n\r\n", + content_disposition, + content_type)); + + result.push_str(from_utf8(&f.upcast::<Blob>().get_data().get_bytes()).unwrap()); + } + } + } + + result.push_str(&*format!("\r\n--{}--", boundary)); + + return result; + } + /// [Form submission](https://html.spec.whatwg.org/multipage/#concept-form-submit) pub fn submit(&self, submit_method_flag: SubmittedFrom, submitter: FormSubmitter) { // Step 1 @@ -264,7 +351,7 @@ impl HTMLFormElement { } } // Step 6 - let form_data = self.get_form_dataset(Some(submitter)); + let mut form_data = self.get_form_dataset(Some(submitter)); // Step 7 let mut action = submitter.action(); // Step 8 @@ -287,13 +374,21 @@ impl HTMLFormElement { let parsed_data = match enctype { FormEncType::UrlEncoded => { - let mime: mime::Mime = "application/x-www-form-urlencoded".parse().unwrap(); - load_data.headers.set(ContentType(mime)); + load_data.headers.set(ContentType::form_url_encoded()); + form_urlencoded::Serializer::new(String::new()) - .extend_pairs(form_data.into_iter().map(|field| (field.name, field.value))) + .extend_pairs(form_data.into_iter().map(|field| (field.name.clone(), field.value_str()))) .finish() } - _ => "".to_owned() // TODO: Add serializers for the other encoding types + FormEncType::FormDataEncoded => { + let boundary = self.generate_boundary(); + let mime = mime!(Multipart / FormData; Boundary =(&boundary)); + load_data.headers.set(ContentType(mime)); + + self.encode_form_data(&mut form_data, None, boundary) + } + // TODO: Support plain text encoding + FormEncType::TextPlainEncoded => "".to_owned() }; // Step 18 @@ -435,7 +530,7 @@ impl HTMLFormElement { data_set.push(FormDatum { ty: textarea.Type(), name: name, - value: textarea.Value() + value: FormDatumValue::String(textarea.Value()) }); } } @@ -489,7 +584,10 @@ impl HTMLFormElement { "file" | "textarea" => (), _ => { datum.name = clean_crlf(&datum.name); - datum.value = clean_crlf(&datum.value); + datum.value = FormDatumValue::String(clean_crlf( match datum.value { + FormDatumValue::String(ref s) => s, + FormDatumValue::File(_) => unreachable!() + })); } } }; @@ -544,12 +642,25 @@ impl HTMLFormElement { } -// TODO: add file support -#[derive(HeapSizeOf)] +pub enum FormDatumValue { + File(Root<File>), + String(DOMString) +} + +// #[derive(HeapSizeOf)] pub struct FormDatum { pub ty: DOMString, pub name: DOMString, - pub value: DOMString + pub value: FormDatumValue +} + +impl FormDatum { + pub fn value_str(&self) -> String { + match self.value { + FormDatumValue::String(ref s) => String::from(s.clone()), + FormDatumValue::File(ref f) => String::from(f.name().clone()) + } + } } #[derive(Copy, Clone, HeapSizeOf)] diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index d42709b487d..0e675b35b27 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -22,7 +22,7 @@ use dom::event::{Event, EventBubbles, EventCancelable}; use dom::eventtarget::EventTarget; use dom::htmlelement::HTMLElement; use dom::htmlfieldsetelement::HTMLFieldSetElement; -use dom::htmlformelement::{FormControl, FormDatum, FormSubmitter, HTMLFormElement}; +use dom::htmlformelement::{FormDatumValue, FormControl, FormDatum, FormSubmitter, HTMLFormElement}; use dom::htmlformelement::{ResetFrom, SubmittedFrom}; use dom::keyboardevent::KeyboardEvent; use dom::node::{Node, NodeDamage, UnbindContext}; @@ -625,6 +625,7 @@ impl HTMLInputElement { atom!("radio") | atom!("checkbox") => if !self.Checked() || name.is_empty() { return None; }, + atom!("image") | atom!("file") => return None, // Unimplemented // Step 3.1: it's not the "Image Button" and doesn't have a name attribute. _ => if name.is_empty() { @@ -637,7 +638,7 @@ impl HTMLInputElement { Some(FormDatum { ty: DOMString::from(&*ty), // FIXME(ajeffrey): Convert directly from Atoms to DOMStrings name: name, - value: self.Value() + value: FormDatumValue::String(self.Value()) }) } diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs index 45161f5ee2d..cfce718bcbe 100644 --- a/components/script/dom/htmlselectelement.rs +++ b/components/script/dom/htmlselectelement.rs @@ -14,7 +14,7 @@ use dom::document::Document; use dom::element::{AttributeMutation, Element}; use dom::htmlelement::HTMLElement; use dom::htmlfieldsetelement::HTMLFieldSetElement; -use dom::htmlformelement::{FormControl, FormDatum, HTMLFormElement}; +use dom::htmlformelement::{FormDatumValue, FormControl, FormDatum, HTMLFormElement}; use dom::htmloptionelement::HTMLOptionElement; use dom::node::{Node, UnbindContext, window_from_node}; use dom::nodelist::NodeList; @@ -94,7 +94,7 @@ impl HTMLSelectElement { data_set.push(FormDatum { ty: self.Type(), name: self.Name(), - value: opt.Value() + value: FormDatumValue::String(opt.Value()) }); } } diff --git a/components/script/lib.rs b/components/script/lib.rs index dde2063cfc3..05700431c3d 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -51,6 +51,8 @@ extern crate js; extern crate libc; #[macro_use] extern crate log; +#[macro_use] +extern crate mime; extern crate msg; extern crate net_traits; extern crate num_traits; diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 4a38102b74a..daae5d70eec 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -1812,6 +1812,7 @@ dependencies = [ "js 0.1.2 (git+https://github.com/servo/rust-mozjs)", "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 146728abbb9..be0795891e8 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -1681,6 +1681,7 @@ dependencies = [ "js 0.1.2 (git+https://github.com/servo/rust-mozjs)", "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index c2992320eab..7884dc59283 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -1664,6 +1664,7 @@ dependencies = [ "js 0.1.2 (git+https://github.com/servo/rust-mozjs)", "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "net_traits 0.0.1", "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", |