use dom_struct::dom_struct;
use html5ever::LocalName;
use js::rust::HandleObject;
use script_traits::serializable::BlobImpl;
use super::bindings::trace::NoTrace;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::FormDataBinding::FormDataMethods;
use crate::dom::bindings::codegen::UnionTypes::FileOrUSVString;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::iterable::Iterable;
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::blob::Blob;
use crate::dom::file::File;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlformelement::{FormDatum, FormDatumValue, HTMLFormElement};
use crate::script_runtime::CanGc;
#[dom_struct]
pub struct FormData {
reflector_: Reflector,
data: DomRefCell<Vec<(NoTrace<LocalName>, FormDatum)>>,
}
impl FormData {
fn new_inherited(form_datums: Option<Vec<FormDatum>>) -> FormData {
let data = match form_datums {
Some(data) => data
.iter()
.map(|datum| (NoTrace(LocalName::from(datum.name.as_ref())), datum.clone()))
.collect::<Vec<(NoTrace<LocalName>, FormDatum)>>(),
None => Vec::new(),
};
FormData {
reflector_: Reflector::new(),
data: DomRefCell::new(data),
}
}
pub fn new(
form_datums: Option<Vec<FormDatum>>,
global: &GlobalScope,
can_gc: CanGc,
) -> DomRoot<FormData> {
Self::new_with_proto(form_datums, global, None, can_gc)
}
fn new_with_proto(
form_datums: Option<Vec<FormDatum>>,
global: &GlobalScope,
proto: Option<HandleObject>,
can_gc: CanGc,
) -> DomRoot<FormData> {
reflect_dom_object_with_proto(
Box::new(FormData::new_inherited(form_datums)),
global,
proto,
can_gc,
)
}
}
impl FormDataMethods for FormData {
fn Constructor(
global: &GlobalScope,
proto: Option<HandleObject>,
can_gc: CanGc,
form: Option<&HTMLFormElement>,
) -> Fallible<DomRoot<FormData>> {
if let Some(opt_form) = form {
return match opt_form.get_form_dataset(None, None, can_gc) {
Some(form_datums) => Ok(FormData::new_with_proto(
Some(form_datums),
global,
proto,
can_gc,
)),
None => Err(Error::InvalidState),
};
}
Ok(FormData::new_with_proto(None, global, proto, can_gc))
}
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)),
};
self.data
.borrow_mut()
.push((NoTrace(LocalName::from(name.0)), datum));
}
#[allow(crown::unrooted_must_root)]
fn Append_(&self, name: USVString, blob: &Blob, filename: Option<USVString>) {
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,
CanGc::note(),
))),
};
self.data
.borrow_mut()
.push((NoTrace(LocalName::from(name.0)), datum));
}
fn Delete(&self, name: USVString) {
self.data
.borrow_mut()
.retain(|(datum_name, _)| datum_name.0 != name.0);
}
fn Get(&self, name: USVString) -> Option<FileOrUSVString> {
self.data
.borrow()
.iter()
.find(|(datum_name, _)| datum_name.0 == name.0)
.map(|(_, datum)| match &datum.value {
FormDatumValue::String(ref s) => {
FileOrUSVString::USVString(USVString(s.to_string()))
},
FormDatumValue::File(ref b) => FileOrUSVString::File(DomRoot::from_ref(b)),
})
}
fn GetAll(&self, name: USVString) -> Vec<FileOrUSVString> {
self.data
.borrow()
.iter()
.filter_map(|(datum_name, datum)| {
if datum_name.0 != name.0 {
return None;
}
Some(match &datum.value {
FormDatumValue::String(ref s) => {
FileOrUSVString::USVString(USVString(s.to_string()))
},
FormDatumValue::File(ref b) => FileOrUSVString::File(DomRoot::from_ref(b)),
})
})
.collect()
}
fn Has(&self, name: USVString) -> bool {
self.data
.borrow()
.iter()
.any(|(datum_name, _0)| datum_name.0 == name.0)
}
fn Set(&self, name: USVString, str_value: USVString) {
let mut data = self.data.borrow_mut();
let local_name = LocalName::from(name.0.clone());
data.retain(|(datum_name, _)| datum_name.0 != local_name);
data.push((
NoTrace(local_name),
FormDatum {
ty: DOMString::from("string"),
name: DOMString::from(name.0),
value: FormDatumValue::String(DOMString::from(str_value.0)),
},
));
}
#[allow(crown::unrooted_must_root)]
fn Set_(&self, name: USVString, blob: &Blob, filename: Option<USVString>) {
let file = self.create_an_entry(blob, filename, CanGc::note());
let mut data = self.data.borrow_mut();
let local_name = LocalName::from(name.0.clone());
data.retain(|(datum_name, _)| datum_name.0 != local_name);
data.push((
NoTrace(LocalName::from(name.0.clone())),
FormDatum {
ty: DOMString::from("file"),
name: DOMString::from(name.0),
value: FormDatumValue::File(file),
},
));
}
}
impl FormData {
fn create_an_entry(
&self,
blob: &Blob,
opt_filename: Option<USVString>,
can_gc: CanGc,
) -> DomRoot<File> {
let name = match opt_filename {
Some(filename) => DOMString::from(filename.0),
None => match blob.downcast::<File>() {
None => DOMString::from("blob"),
Some(file) => {
return DomRoot::from_ref(file);
},
},
};
let bytes = blob.get_bytes().unwrap_or_default();
File::new(
&self.global(),
BlobImpl::new_from_bytes(bytes, blob.type_string()),
name,
None,
can_gc,
)
}
pub fn datums(&self) -> Vec<FormDatum> {
self.data
.borrow()
.iter()
.map(|(_, datum)| datum.clone())
.collect()
}
}
impl Iterable for FormData {
type Key = USVString;
type Value = FileOrUSVString;
fn get_iterable_length(&self) -> u32 {
self.data.borrow().len() as u32
}
fn get_value_at_index(&self, n: u32) -> FileOrUSVString {
let data = self.data.borrow();
let datum = &data.get(n as usize).unwrap().1;
match &datum.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 key = &data.get(n as usize).unwrap().0;
USVString(key.to_string())
}
}