Implement formdata event

This commit is contained in:
CYBAI 2019-01-09 18:57:35 +08:00
parent 477b6ef886
commit 9d70f51356
13 changed files with 189 additions and 90 deletions

View file

@ -30,6 +30,7 @@ error
fantasy fantasy
fetch fetch
file file
formdata
fullscreenchange fullscreenchange
fullscreenerror fullscreenerror
gattserverdisconnected gattserverdisconnected

View file

@ -6,7 +6,7 @@ use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::FormDataBinding::FormDataMethods; use crate::dom::bindings::codegen::Bindings::FormDataBinding::FormDataMethods;
use crate::dom::bindings::codegen::Bindings::FormDataBinding::FormDataWrap; use crate::dom::bindings::codegen::Bindings::FormDataBinding::FormDataWrap;
use crate::dom::bindings::codegen::UnionTypes::FileOrUSVString; use crate::dom::bindings::codegen::UnionTypes::FileOrUSVString;
use crate::dom::bindings::error::Fallible; use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::iterable::Iterable; use crate::dom::bindings::iterable::Iterable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
@ -26,10 +26,9 @@ pub struct FormData {
} }
impl FormData { impl FormData {
fn new_inherited(opt_form: Option<&HTMLFormElement>) -> FormData { fn new_inherited(form_datums: Option<Vec<FormDatum>>) -> FormData {
let data = match opt_form { let data = match form_datums {
Some(form) => form Some(data) => data
.get_form_dataset(None)
.iter() .iter()
.map(|datum| (LocalName::from(datum.name.as_ref()), datum.clone())) .map(|datum| (LocalName::from(datum.name.as_ref()), datum.clone()))
.collect::<Vec<(LocalName, FormDatum)>>(), .collect::<Vec<(LocalName, FormDatum)>>(),
@ -42,20 +41,27 @@ impl FormData {
} }
} }
pub fn new(form: Option<&HTMLFormElement>, global: &GlobalScope) -> DomRoot<FormData> { pub fn new(form_datums: Option<Vec<FormDatum>>, global: &GlobalScope) -> DomRoot<FormData> {
reflect_dom_object( reflect_dom_object(
Box::new(FormData::new_inherited(form)), Box::new(FormData::new_inherited(form_datums)),
global, global,
FormDataWrap, FormDataWrap,
) )
} }
// https://xhr.spec.whatwg.org/#dom-formdata
pub fn Constructor( pub fn Constructor(
global: &GlobalScope, global: &GlobalScope,
form: Option<&HTMLFormElement>, form: Option<&HTMLFormElement>,
) -> Fallible<DomRoot<FormData>> { ) -> Fallible<DomRoot<FormData>> {
// TODO: Construct form data set for form if it is supplied if let Some(opt_form) = form {
Ok(FormData::new(form, global)) return match opt_form.get_form_dataset(None) {
Some(form_datums) => Ok(FormData::new(Some(form_datums), global)),
None => Err(Error::InvalidState),
};
}
Ok(FormData::new(None, global))
} }
} }

View file

@ -0,0 +1,90 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use crate::dom::bindings::codegen::Bindings::FormDataEventBinding;
use crate::dom::bindings::codegen::Bindings::FormDataEventBinding::FormDataEventMethods;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::event::Event;
use crate::dom::event::{EventBubbles, EventCancelable};
use crate::dom::formdata::FormData;
use crate::dom::globalscope::GlobalScope;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use servo_atoms::Atom;
#[dom_struct]
pub struct FormDataEvent {
event: Event,
form_data: Dom<FormData>,
}
impl FormDataEvent {
pub fn new(
global: &GlobalScope,
type_: Atom,
can_bubble: EventBubbles,
cancelable: EventCancelable,
form_data: &FormData,
) -> DomRoot<FormDataEvent> {
let ev = reflect_dom_object(
Box::new(FormDataEvent {
event: Event::new_inherited(),
form_data: Dom::from_ref(form_data),
}),
global,
FormDataEventBinding::Wrap,
);
{
let event = ev.upcast::<Event>();
event.init_event(type_, bool::from(can_bubble), bool::from(cancelable));
}
ev
}
pub fn Constructor(
window: &Window,
type_: DOMString,
init: &FormDataEventBinding::FormDataEventInit,
) -> Fallible<DomRoot<FormDataEvent>> {
let bubbles = EventBubbles::from(init.parent.bubbles);
let cancelable = EventCancelable::from(init.parent.cancelable);
let form_data = match init.formData {
Some(ref form_data) => form_data.clone(),
None => {
return Err(Error::Type(
"required member formData is undefined".to_string(),
));
},
};
let event = FormDataEvent::new(
&window.global(),
Atom::from(type_),
bubbles,
cancelable,
&*form_data,
);
Ok(event)
}
}
impl FormDataEventMethods for FormDataEvent {
// https://html.spec.whatwg.org/multipage/#dom-formdataevent-formdata
fn FormData(&self) -> DomRoot<FormData> {
DomRoot::from_ref(&*self.form_data)
}
// https://dom.spec.whatwg.org/#dom-event-istrusted
fn IsTrusted(&self) -> bool {
self.event.IsTrusted()
}
}

View file

@ -20,8 +20,11 @@ use crate::dom::bindings::str::DOMString;
use crate::dom::blob::Blob; use crate::dom::blob::Blob;
use crate::dom::document::Document; use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element}; use crate::dom::element::{AttributeMutation, Element};
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
use crate::dom::file::File; use crate::dom::file::File;
use crate::dom::formdata::FormData;
use crate::dom::formdataevent::FormDataEvent;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlbuttonelement::HTMLButtonElement; use crate::dom::htmlbuttonelement::HTMLButtonElement;
use crate::dom::htmlcollection::CollectionFilter; use crate::dom::htmlcollection::CollectionFilter;
@ -68,6 +71,8 @@ pub struct GenerationId(u32);
pub struct HTMLFormElement { pub struct HTMLFormElement {
htmlelement: HTMLElement, htmlelement: HTMLElement,
marked_for_reset: Cell<bool>, marked_for_reset: Cell<bool>,
/// https://html.spec.whatwg.org/multipage/#constructing-entry-list
constructing_entry_list: Cell<bool>,
elements: DomOnceCell<HTMLFormControlsCollection>, elements: DomOnceCell<HTMLFormControlsCollection>,
generation_id: Cell<GenerationId>, generation_id: Cell<GenerationId>,
controls: DomRefCell<Vec<Dom<Element>>>, controls: DomRefCell<Vec<Dom<Element>>>,
@ -82,6 +87,7 @@ impl HTMLFormElement {
HTMLFormElement { HTMLFormElement {
htmlelement: HTMLElement::new_inherited(local_name, prefix, document), htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
marked_for_reset: Cell::new(false), marked_for_reset: Cell::new(false),
constructing_entry_list: Cell::new(false),
elements: Default::default(), elements: Default::default(),
generation_id: Cell::new(GenerationId(0)), generation_id: Cell::new(GenerationId(0)),
controls: DomRefCell::new(Vec::new()), controls: DomRefCell::new(Vec::new()),
@ -311,11 +317,16 @@ impl HTMLFormElement {
/// [Form submission](https://html.spec.whatwg.org/multipage/#concept-form-submit) /// [Form submission](https://html.spec.whatwg.org/multipage/#concept-form-submit)
pub fn submit(&self, submit_method_flag: SubmittedFrom, submitter: FormSubmitter) { pub fn submit(&self, submit_method_flag: SubmittedFrom, submitter: FormSubmitter) {
// Step 1 // TODO: Step 1. If form cannot navigate , then return.
// Step 2
if self.constructing_entry_list.get() {
return;
}
// Step 3
let doc = document_from_node(self); let doc = document_from_node(self);
let base = doc.base_url(); let base = doc.base_url();
// TODO: Handle browsing contexts (Step 2, 3) // TODO: Handle browsing contexts (Step 4, 5)
// Step 4 // Step 6
if submit_method_flag == SubmittedFrom::NotFromForm && !submitter.no_validate(self) { if submit_method_flag == SubmittedFrom::NotFromForm && !submitter.no_validate(self) {
if self.interactive_validation().is_err() { if self.interactive_validation().is_err() {
// TODO: Implement event handlers on all form control elements // TODO: Implement event handlers on all form control elements
@ -323,7 +334,7 @@ impl HTMLFormElement {
return; return;
} }
} }
// Step 5 // Step 7
if submit_method_flag == SubmittedFrom::NotFromForm { if submit_method_flag == SubmittedFrom::NotFromForm {
let event = self let event = self
.upcast::<EventTarget>() .upcast::<EventTarget>()
@ -332,30 +343,36 @@ impl HTMLFormElement {
return; return;
} }
} }
// Step 6
let mut form_data = self.get_form_dataset(Some(submitter));
// Step 7
let encoding = self.pick_encoding();
// Step 8 // Step 8
let mut action = submitter.action(); let encoding = self.pick_encoding();
// Step 9 // Step 9
let mut form_data = match self.get_form_dataset(Some(submitter)) {
Some(form_data) => form_data,
None => return,
};
// TODO: Step 10. If form cannot navigate, then return.
// Step 11
let mut action = submitter.action();
// Step 12
if action.is_empty() { if action.is_empty() {
action = DOMString::from(base.as_str()); action = DOMString::from(base.as_str());
} }
// Step 10-11 // Step 13-14
let action_components = match base.join(&action) { let action_components = match base.join(&action) {
Ok(url) => url, Ok(url) => url,
Err(_) => return, Err(_) => return,
}; };
// Step 12-15 // Step 15-17
let scheme = action_components.scheme().to_owned(); let scheme = action_components.scheme().to_owned();
let enctype = submitter.enctype(); let enctype = submitter.enctype();
let method = submitter.method(); let method = submitter.method();
// Step 16, 17 // Step 18-21
let target_attribute_value = submitter.target(); let target_attribute_value = submitter.target();
let source = doc.browsing_context().unwrap(); let source = doc.browsing_context().unwrap();
let (maybe_chosen, _new) = source.choose_browsing_context(target_attribute_value, false); let (maybe_chosen, _new) = source.choose_browsing_context(target_attribute_value, false);
@ -375,7 +392,7 @@ impl HTMLFormElement {
Some(target_document.url()), Some(target_document.url()),
); );
// Step 18 // Step 22
match (&*scheme, method) { match (&*scheme, method) {
(_, FormMethod::FormDialog) => { (_, FormMethod::FormDialog) => {
// TODO: Submit dialog // TODO: Submit dialog
@ -597,18 +614,18 @@ impl HTMLFormElement {
} }
/// <https://html.spec.whatwg.org/multipage/#constructing-the-form-data-set> /// <https://html.spec.whatwg.org/multipage/#constructing-the-form-data-set>
/// Steps range from 1 to 3 /// Steps range from 3 to 5
fn get_unclean_dataset(&self, submitter: Option<FormSubmitter>) -> Vec<FormDatum> { fn get_unclean_dataset(&self, submitter: Option<FormSubmitter>) -> Vec<FormDatum> {
let controls = self.controls.borrow(); let controls = self.controls.borrow();
let mut data_set = Vec::new(); let mut data_set = Vec::new();
for child in controls.iter() { for child in controls.iter() {
// Step 3.1: The field element is disabled. // Step 5.1: The field element is disabled.
if child.disabled_state() { if child.disabled_state() {
continue; continue;
} }
let child = child.upcast::<Node>(); let child = child.upcast::<Node>();
// Step 3.1: The field element has a datalist element ancestor. // Step 5.1: The field element has a datalist element ancestor.
if child if child
.ancestors() .ancestors()
.any(|a| DomRoot::downcast::<HTMLDataListElement>(a).is_some()) .any(|a| DomRoot::downcast::<HTMLDataListElement>(a).is_some())
@ -657,7 +674,7 @@ impl HTMLFormElement {
} }
/// <https://html.spec.whatwg.org/multipage/#constructing-the-form-data-set> /// <https://html.spec.whatwg.org/multipage/#constructing-the-form-data-set>
pub fn get_form_dataset(&self, submitter: Option<FormSubmitter>) -> Vec<FormDatum> { pub fn get_form_dataset(&self, submitter: Option<FormSubmitter>) -> Option<Vec<FormDatum>> {
fn clean_crlf(s: &str) -> DOMString { fn clean_crlf(s: &str) -> DOMString {
// Step 4 // Step 4
let mut buf = "".to_owned(); let mut buf = "".to_owned();
@ -689,9 +706,16 @@ impl HTMLFormElement {
DOMString::from(buf) DOMString::from(buf)
} }
// Step 1-3 // Step 1
if self.constructing_entry_list.get() {
return None;
}
// Step 2
self.constructing_entry_list.set(true);
// Step 3-6
let mut ret = self.get_unclean_dataset(submitter); let mut ret = self.get_unclean_dataset(submitter);
// Step 4
for datum in &mut ret { for datum in &mut ret {
match &*datum.ty { match &*datum.ty {
"file" | "textarea" => (), // TODO "file" | "textarea" => (), // TODO
@ -704,8 +728,28 @@ impl HTMLFormElement {
}, },
} }
} }
// Step 5
ret let window = window_from_node(self);
// Step 6
let form_data = FormData::new(Some(ret), &window.global());
// Step 7
let event = FormDataEvent::new(
&window.global(),
atom!("formdata"),
EventBubbles::Bubbles,
EventCancelable::NotCancelable,
&form_data,
);
event.upcast::<Event>().fire(self.upcast::<EventTarget>());
// Step 8
self.constructing_entry_list.set(false);
// Step 9
Some(form_data.datums())
} }
pub fn reset(&self, _reset_method_flag: ResetFrom) { pub fn reset(&self, _reset_method_flag: ResetFrom) {

View file

@ -303,6 +303,7 @@ pub mod filereader;
pub mod filereadersync; pub mod filereadersync;
pub mod focusevent; pub mod focusevent;
pub mod formdata; pub mod formdata;
pub mod formdataevent;
pub mod gainnode; pub mod gainnode;
pub mod gamepad; pub mod gamepad;
pub mod gamepadbutton; pub mod gamepadbutton;

View file

@ -0,0 +1,14 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://html.spec.whatwg.org/multipage/#the-formdataevent-interface
[Exposed=Window,
Constructor(DOMString type, optional FormDataEventInit eventInitDict)]
interface FormDataEvent : Event {
readonly attribute FormData formData;
};
dictionary FormDataEventInit : EventInit {
/*required*/ FormData formData;
};

View file

@ -10940,36 +10940,6 @@
[ImageData interface: new ImageData(10, 10) must inherit property "data" with the proper type] [ImageData interface: new ImageData(10, 10) must inherit property "data" with the proper type]
expected: FAIL expected: FAIL
[FormDataEvent interface object name]
expected: FAIL
[FormDataEvent interface: existence and properties of interface prototype object's "constructor" property]
expected: FAIL
[FormDataEvent interface: existence and properties of interface prototype object's @@unscopables property]
expected: FAIL
[FormDataEvent interface: attribute formData]
expected: FAIL
[Stringification of new FormDataEvent("formdata", { formData: new FormData() })]
expected: FAIL
[FormDataEvent interface: new FormDataEvent("formdata", { formData: new FormData() }) must inherit property "formData" with the proper type]
expected: FAIL
[FormDataEvent interface: existence and properties of interface object]
expected: FAIL
[FormDataEvent interface object length]
expected: FAIL
[FormDataEvent must be primary interface of new FormDataEvent("formdata", { formData: new FormData() })]
expected: FAIL
[FormDataEvent interface: existence and properties of interface prototype object]
expected: FAIL
[OffscreenCanvasRenderingContext2D interface: operation measureText(DOMString)] [OffscreenCanvasRenderingContext2D interface: operation measureText(DOMString)]
expected: FAIL expected: FAIL

View file

@ -1,7 +0,0 @@
[FormDataEvent.window.html]
[Successful FormDataEvent constructor]
expected: FAIL
[Failing FormDataEvent constructor]
expected: FAIL

View file

@ -10,13 +10,3 @@
[Entries added to "formData" IDL attribute should be submitted.] [Entries added to "formData" IDL attribute should be submitted.]
expected: FAIL expected: FAIL
["formData" IDL attribute should have entries for form-associated elements in the first event handler, and the second handler can read entries set by the first handler.]
expected: FAIL
["formdata" event bubbles, and is not cancelable.]
expected: FAIL
["formdata" event bubbles in an orphan tree.]
expected: FAIL

View file

@ -1,4 +0,0 @@
[form-submission-algorithm.html]
[If constructing entry list flag of form is true, then return]
expected: FAIL

View file

@ -1,7 +0,0 @@
[formdata.htm]
[|new FormData()| in formdata event handler should throw]
expected: FAIL
[Newly created FormData contains entries added to "formData" IDL attribute of FormDataEvent.]
expected: FAIL

View file

@ -27102,7 +27102,7 @@
"testharness" "testharness"
], ],
"mozilla/interfaces.html": [ "mozilla/interfaces.html": [
"95ab0109c82c8e90a3e53a3579b9337e2091e26c", "55cafc9995da83d48230eed5b5c3863d8cf173c9",
"testharness" "testharness"
], ],
"mozilla/interfaces.js": [ "mozilla/interfaces.js": [

View file

@ -77,6 +77,7 @@ test_interfaces([
"FileReader", "FileReader",
"FocusEvent", "FocusEvent",
"FormData", "FormData",
"FormDataEvent",
"GainNode", "GainNode",
"HashChangeEvent", "HashChangeEvent",
"Headers", "Headers",