aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/htmlformelement.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/htmlformelement.rs')
-rw-r--r--components/script/dom/htmlformelement.rs307
1 files changed, 169 insertions, 138 deletions
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index 515d49c0f7c..d6a794621e5 100644
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -12,6 +12,7 @@ use dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMet
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
use dom::bindings::conversions::DerivedFrom;
+use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::refcounted::Trusted;
@@ -36,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};
@@ -51,10 +50,8 @@ use std::cell::Cell;
use std::sync::mpsc::Sender;
use string_cache::Atom;
use style::attr::AttrValue;
+use style::str::split_html_space_chars;
use task_source::TaskSource;
-use task_source::dom_manipulation::DOMManipulationTask;
-use url::form_urlencoded;
-use util::str::split_html_space_chars;
#[derive(JSTraceable, PartialEq, Clone, Copy, HeapSizeOf)]
pub struct GenerationId(u32);
@@ -68,11 +65,11 @@ pub struct HTMLFormElement {
}
impl HTMLFormElement {
- fn new_inherited(localName: Atom,
+ fn new_inherited(local_name: Atom,
prefix: Option<DOMString>,
document: &Document) -> HTMLFormElement {
HTMLFormElement {
- htmlelement: HTMLElement::new_inherited(localName, prefix, document),
+ htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
marked_for_reset: Cell::new(false),
elements: Default::default(),
generation_id: Cell::new(GenerationId(0))
@@ -80,11 +77,12 @@ impl HTMLFormElement {
}
#[allow(unrooted_must_root)]
- pub fn new(localName: Atom,
+ pub fn new(local_name: Atom,
prefix: Option<DOMString>,
document: &Document) -> Root<HTMLFormElement> {
- let element = HTMLFormElement::new_inherited(localName, prefix, document);
- Node::reflect_node(box element, document, HTMLFormElementBinding::Wrap)
+ Node::reflect_node(box HTMLFormElement::new_inherited(local_name, prefix, document),
+ document,
+ HTMLFormElementBinding::Wrap)
}
pub fn generation_id(&self) -> GenerationId {
@@ -232,9 +230,9 @@ impl HTMLFormElementMethods for HTMLFormElement {
}
// https://html.spec.whatwg.org/multipage/#dom-form-item
- fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<Element>> {
+ fn IndexedGetter(&self, index: u32) -> Option<Root<Element>> {
let elements = self.Elements();
- elements.IndexedGetter(index, found)
+ elements.IndexedGetter(index)
}
}
@@ -252,13 +250,6 @@ 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
@@ -277,67 +268,6 @@ impl HTMLFormElement {
document_from_node(self).encoding()
}
- // 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 {
- // 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_or("UTF-8");
-
- // 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()));
- // 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 slice = f.upcast::<Blob>().get_slice_or_empty();
-
- let decoded = encoding.decode(&slice.get_bytes(), DecoderTrap::Replace)
- .expect("Invalid encoding in file");
- result.push_str(&decoded);
- }
- }
- }
-
- result.push_str(&*format!("\r\n--{}--", boundary));
-
- result
- }
-
// https://html.spec.whatwg.org/multipage/#text/plain-encoding-algorithm
fn encode_plaintext(&self, form_data: &mut Vec<FormDatum>) -> String {
// Step 1
@@ -350,18 +280,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
@@ -373,7 +296,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)
@@ -396,13 +319,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
@@ -416,57 +344,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 = 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)
+ 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
@@ -477,15 +435,14 @@ impl HTMLFormElement {
// Step 2
let nav = box PlannedNavigation {
load_data: load_data,
- pipeline_id: window.pipeline(),
+ pipeline_id: window.pipeline_id(),
script_chan: window.main_thread_script_chan().clone(),
generation_id: self.generation_id.get(),
form: Trusted::new(self)
};
// Step 3
- window.dom_manipulation_task_source().queue(
- DOMManipulationTask::PlannedNavigation(nav)).unwrap();
+ window.dom_manipulation_task_source().queue(nav, GlobalRef::Window(&window)).unwrap();
}
/// Interactively validate the constraints of form elements
@@ -558,10 +515,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();
@@ -696,12 +651,13 @@ impl HTMLFormElement {
}
+#[derive(JSTraceable, HeapSizeOf, Clone)]
pub enum FormDatumValue {
File(Root<File>),
String(DOMString)
}
-// #[derive(HeapSizeOf)]
+#[derive(HeapSizeOf, JSTraceable, Clone)]
pub struct FormDatum {
pub ty: DOMString,
pub name: DOMString,
@@ -709,10 +665,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())
}
}
}
@@ -937,6 +897,8 @@ struct PlannedNavigation {
}
impl Runnable for PlannedNavigation {
+ fn name(&self) -> &'static str { "PlannedNavigation" }
+
fn handler(self: Box<PlannedNavigation>) {
if self.generation_id == self.form.root().generation_id.get() {
let script_chan = self.script_chan.clone();
@@ -944,3 +906,72 @@ impl Runnable for PlannedNavigation {
}
}
}
+
+
+// https://html.spec.whatwg.org/multipage/#multipart/form-data-encoding-algorithm
+pub fn encode_multipart_form_data(form_data: &mut Vec<FormDatum>,
+ boundary: String, encoding: EncodingRef) -> Vec<u8> {
+ // Step 1
+ let mut result = vec![];
+
+ // Step 2
+ let charset = &*encoding.whatwg_name().unwrap_or("UTF-8");
+
+ // Step 3
+ for entry in form_data.iter_mut() {
+ // 3.1
+ if entry.name == "_charset_" && entry.ty == "hidden" {
+ entry.value = FormDatumValue::String(DOMString::from(charset.clone()));
+ }
+ // TODO: 3.2
+
+ // Step 4
+ // https://tools.ietf.org/html/rfc7578#section-4
+ // 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) => {
+ 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())),
+ None,
+ f.name().clone().into()));
+ // https://tools.ietf.org/html/rfc7578#section-4.4
+ let content_type = ContentType(f.upcast::<Blob>().Type()
+ .parse().unwrap_or(mime!(Text / Plain)));
+ let mut type_bytes = format!("Content-Disposition: {}\r\ncontent-type: {}\r\n\r\n",
+ content_disposition,
+ content_type).into_bytes();
+ result.append(&mut type_bytes);
+
+ let mut bytes = f.upcast::<Blob>().get_bytes().unwrap_or(vec![]);
+
+ result.append(&mut bytes);
+ }
+ }
+ }
+
+ let mut boundary_bytes = format!("\r\n--{}--", boundary).into_bytes();
+ result.append(&mut boundary_bytes);
+
+ result
+}
+
+// https://tools.ietf.org/html/rfc7578#section-4.1
+pub fn generate_boundary() -> String {
+ let i1 = random::<u32>();
+ let i2 = random::<u32>();
+
+ format!("---------------------------{0}{1}", i1, i2)
+}