/* 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/. */ use dom::bindings::cell::DomRefCell; use dom::bindings::codegen::Bindings::FormDataBinding::FormDataMethods; use dom::bindings::codegen::Bindings::FormDataBinding::FormDataWrap; use dom::bindings::codegen::UnionTypes::FileOrUSVString; use dom::bindings::error::Fallible; use dom::bindings::inheritance::Castable; use dom::bindings::iterable::Iterable; use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; use dom::bindings::root::DomRoot; use dom::bindings::str::{DOMString, USVString}; use dom::blob::{Blob, BlobImpl}; use dom::file::File; use dom::globalscope::GlobalScope; use dom::htmlformelement::{HTMLFormElement, FormDatumValue, FormDatum}; use dom_struct::dom_struct; use html5ever::LocalName; use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::iter; #[dom_struct] pub struct FormData { reflector_: Reflector, data: DomRefCell>>, } impl FormData { fn new_inherited(opt_form: Option<&HTMLFormElement>) -> FormData { let mut hashmap: HashMap> = HashMap::new(); if let Some(form) = opt_form { for datum in form.get_form_dataset(None) { match hashmap.entry(LocalName::from(datum.name.as_ref())) { Occupied(entry) => entry.into_mut().push(datum), Vacant(entry) => { entry.insert(vec!(datum)); } } } } FormData { reflector_: Reflector::new(), data: DomRefCell::new(hashmap), } } pub fn new(form: Option<&HTMLFormElement>, global: &GlobalScope) -> DomRoot { reflect_dom_object(Box::new(FormData::new_inherited(form)), global, FormDataWrap) } pub fn Constructor(global: &GlobalScope, form: Option<&HTMLFormElement>) -> Fallible> { // TODO: Construct form data set for form if it is supplied Ok(FormData::new(form, global)) } } impl FormDataMethods for FormData { // https://xhr.spec.whatwg.org/#dom-formdata-append fn Append(&self, name: USVString, str_value: USVString) { let datum = FormDatum { ty: DOMString::from("string"), name: DOMString::from(name.0.clone()), value: FormDatumValue::String(DOMString::from(str_value.0)), }; let mut data = self.data.borrow_mut(); match data.entry(LocalName::from(name.0)) { Occupied(entry) => entry.into_mut().push(datum), Vacant(entry) => { entry.insert(vec!(datum)); } } } #[allow(unrooted_must_root)] // https://xhr.spec.whatwg.org/#dom-formdata-append fn Append_(&self, name: USVString, blob: &Blob, filename: Option) { let datum = FormDatum { ty: DOMString::from("file"), name: DOMString::from(name.0.clone()), value: FormDatumValue::File(DomRoot::from_ref(&*self.create_an_entry(blob, filename))), }; let mut data = self.data.borrow_mut(); match data.entry(LocalName::from(name.0)) { Occupied(entry) => entry.into_mut().push(datum), Vacant(entry) => { entry.insert(vec!(datum)); }, } } // https://xhr.spec.whatwg.org/#dom-formdata-delete fn Delete(&self, name: USVString) { self.data.borrow_mut().remove(&LocalName::from(name.0)); } // https://xhr.spec.whatwg.org/#dom-formdata-get fn Get(&self, name: USVString) -> Option { self.data.borrow() .get(&LocalName::from(name.0)) .map(|entry| match entry[0].value { FormDatumValue::String(ref s) => FileOrUSVString::USVString(USVString(s.to_string())), FormDatumValue::File(ref b) => FileOrUSVString::File(DomRoot::from_ref(&*b)), }) } // https://xhr.spec.whatwg.org/#dom-formdata-getall fn GetAll(&self, name: USVString) -> Vec { self.data.borrow() .get(&LocalName::from(name.0)) .map_or(vec![], |data| data.iter().map(|item| match item.value { FormDatumValue::String(ref s) => FileOrUSVString::USVString(USVString(s.to_string())), FormDatumValue::File(ref b) => FileOrUSVString::File(DomRoot::from_ref(&*b)), }).collect() ) } // https://xhr.spec.whatwg.org/#dom-formdata-has fn Has(&self, name: USVString) -> bool { self.data.borrow().contains_key(&LocalName::from(name.0)) } // https://xhr.spec.whatwg.org/#dom-formdata-set fn Set(&self, name: USVString, str_value: USVString) { self.data.borrow_mut().insert(LocalName::from(name.0.clone()), vec![FormDatum { ty: DOMString::from("string"), name: DOMString::from(name.0), value: FormDatumValue::String(DOMString::from(str_value.0)), }]); } #[allow(unrooted_must_root)] // https://xhr.spec.whatwg.org/#dom-formdata-set fn Set_(&self, name: USVString, blob: &Blob, filename: Option) { self.data.borrow_mut().insert(LocalName::from(name.0.clone()), vec![FormDatum { ty: DOMString::from("file"), name: DOMString::from(name.0), value: FormDatumValue::File(DomRoot::from_ref(&*self.create_an_entry(blob, filename))), }]); } } impl FormData { // https://xhr.spec.whatwg.org/#create-an-entry // Steps 3-4. fn create_an_entry(&self, blob: &Blob, opt_filename: Option) -> DomRoot { let name = match opt_filename { Some(filename) => DOMString::from(filename.0), None if blob.downcast::().is_none() => DOMString::from("blob"), None => DOMString::from(""), }; let bytes = blob.get_bytes().unwrap_or(vec![]); File::new(&self.global(), BlobImpl::new_from_bytes(bytes), name, None, &blob.type_string()) } pub fn datums(&self) -> Vec { self.data.borrow().values() .flat_map(|value| value.iter()) .map(|value| value.clone()) .collect() } } impl Iterable for FormData { type Key = USVString; type Value = FileOrUSVString; fn get_iterable_length(&self) -> u32 { self.data.borrow().values().map(|value| value.len()).sum::() as u32 } fn get_value_at_index(&self, n: u32) -> FileOrUSVString { let data = self.data.borrow(); let value = &data.values() .flat_map(|value| value.iter()) .nth(n as usize) .unwrap() .value; match *value { FormDatumValue::String(ref s) => FileOrUSVString::USVString(USVString(s.to_string())), FormDatumValue::File(ref b) => FileOrUSVString::File(DomRoot::from_ref(&*b)), } } fn get_key_at_index(&self, n: u32) -> USVString { let data = self.data.borrow(); let value = &data.iter() .flat_map(|(key, value)| iter::repeat(key).take(value.len())) .nth(n as usize) .unwrap(); USVString(value.to_string()) } }