diff options
author | CYBAI <cyb.ai.815@gmail.com> | 2019-01-09 18:57:35 +0800 |
---|---|---|
committer | CYBAI <cyb.ai.815@gmail.com> | 2019-01-23 00:38:44 +0800 |
commit | 9d70f5135614b8fa0ba4e1322ac114587b959eb5 (patch) | |
tree | fa73d59cfa8b8b4276cd9d586c637e8f2821c61e | |
parent | 477b6ef886401e54fa9dbe4b07298cf5cafc0315 (diff) | |
download | servo-9d70f5135614b8fa0ba4e1322ac114587b959eb5.tar.gz servo-9d70f5135614b8fa0ba4e1322ac114587b959eb5.zip |
Implement formdata event
13 files changed, 188 insertions, 89 deletions
diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt index e14826f8430..db5680256f7 100644 --- a/components/atoms/static_atoms.txt +++ b/components/atoms/static_atoms.txt @@ -30,6 +30,7 @@ error fantasy fetch file +formdata fullscreenchange fullscreenerror gattserverdisconnected diff --git a/components/script/dom/formdata.rs b/components/script/dom/formdata.rs index 2150e2aa7e5..7a084ca0162 100644 --- a/components/script/dom/formdata.rs +++ b/components/script/dom/formdata.rs @@ -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::FormDataWrap; 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::iterable::Iterable; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; @@ -26,10 +26,9 @@ pub struct FormData { } impl FormData { - fn new_inherited(opt_form: Option<&HTMLFormElement>) -> FormData { - let data = match opt_form { - Some(form) => form - .get_form_dataset(None) + fn new_inherited(form_datums: Option<Vec<FormDatum>>) -> FormData { + let data = match form_datums { + Some(data) => data .iter() .map(|datum| (LocalName::from(datum.name.as_ref()), datum.clone())) .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( - Box::new(FormData::new_inherited(form)), + Box::new(FormData::new_inherited(form_datums)), global, FormDataWrap, ) } + // https://xhr.spec.whatwg.org/#dom-formdata pub fn Constructor( global: &GlobalScope, form: Option<&HTMLFormElement>, ) -> Fallible<DomRoot<FormData>> { - // TODO: Construct form data set for form if it is supplied - Ok(FormData::new(form, global)) + if let Some(opt_form) = form { + 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)) } } diff --git a/components/script/dom/formdataevent.rs b/components/script/dom/formdataevent.rs new file mode 100644 index 00000000000..f7ebb57bbfb --- /dev/null +++ b/components/script/dom/formdataevent.rs @@ -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() + } +} diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index 5469f0b7fc5..6a4a4b97fe5 100755 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -20,8 +20,11 @@ use crate::dom::bindings::str::DOMString; use crate::dom::blob::Blob; use crate::dom::document::Document; use crate::dom::element::{AttributeMutation, Element}; +use crate::dom::event::{Event, EventBubbles, EventCancelable}; use crate::dom::eventtarget::EventTarget; use crate::dom::file::File; +use crate::dom::formdata::FormData; +use crate::dom::formdataevent::FormDataEvent; use crate::dom::globalscope::GlobalScope; use crate::dom::htmlbuttonelement::HTMLButtonElement; use crate::dom::htmlcollection::CollectionFilter; @@ -68,6 +71,8 @@ pub struct GenerationId(u32); pub struct HTMLFormElement { htmlelement: HTMLElement, marked_for_reset: Cell<bool>, + /// https://html.spec.whatwg.org/multipage/#constructing-entry-list + constructing_entry_list: Cell<bool>, elements: DomOnceCell<HTMLFormControlsCollection>, generation_id: Cell<GenerationId>, controls: DomRefCell<Vec<Dom<Element>>>, @@ -82,6 +87,7 @@ impl HTMLFormElement { HTMLFormElement { htmlelement: HTMLElement::new_inherited(local_name, prefix, document), marked_for_reset: Cell::new(false), + constructing_entry_list: Cell::new(false), elements: Default::default(), generation_id: Cell::new(GenerationId(0)), controls: DomRefCell::new(Vec::new()), @@ -311,11 +317,16 @@ impl HTMLFormElement { /// [Form submission](https://html.spec.whatwg.org/multipage/#concept-form-submit) 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 base = doc.base_url(); - // TODO: Handle browsing contexts (Step 2, 3) - // Step 4 + // TODO: Handle browsing contexts (Step 4, 5) + // Step 6 if submit_method_flag == SubmittedFrom::NotFromForm && !submitter.no_validate(self) { if self.interactive_validation().is_err() { // TODO: Implement event handlers on all form control elements @@ -323,7 +334,7 @@ impl HTMLFormElement { return; } } - // Step 5 + // Step 7 if submit_method_flag == SubmittedFrom::NotFromForm { let event = self .upcast::<EventTarget>() @@ -332,30 +343,36 @@ impl HTMLFormElement { return; } } - // Step 6 - let mut form_data = self.get_form_dataset(Some(submitter)); - // Step 7 + // Step 8 let encoding = self.pick_encoding(); - // Step 8 + // 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 9 + // Step 12 if action.is_empty() { action = DOMString::from(base.as_str()); } - // Step 10-11 + // Step 13-14 let action_components = match base.join(&action) { Ok(url) => url, Err(_) => return, }; - // Step 12-15 + // Step 15-17 let scheme = action_components.scheme().to_owned(); let enctype = submitter.enctype(); let method = submitter.method(); - // Step 16, 17 + // Step 18-21 let target_attribute_value = submitter.target(); let source = doc.browsing_context().unwrap(); let (maybe_chosen, _new) = source.choose_browsing_context(target_attribute_value, false); @@ -375,7 +392,7 @@ impl HTMLFormElement { Some(target_document.url()), ); - // Step 18 + // Step 22 match (&*scheme, method) { (_, FormMethod::FormDialog) => { // TODO: Submit dialog @@ -597,18 +614,18 @@ impl HTMLFormElement { } /// <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> { let controls = self.controls.borrow(); let mut data_set = Vec::new(); 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() { continue; } 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 .ancestors() .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> - 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 { // Step 4 let mut buf = "".to_owned(); @@ -689,9 +706,16 @@ impl HTMLFormElement { 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); - // Step 4 for datum in &mut ret { match &*datum.ty { "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) { diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index d316000eee4..4d8e89d274f 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -303,6 +303,7 @@ pub mod filereader; pub mod filereadersync; pub mod focusevent; pub mod formdata; +pub mod formdataevent; pub mod gainnode; pub mod gamepad; pub mod gamepadbutton; diff --git a/components/script/dom/webidls/FormDataEvent.webidl b/components/script/dom/webidls/FormDataEvent.webidl new file mode 100644 index 00000000000..d34f57b3a09 --- /dev/null +++ b/components/script/dom/webidls/FormDataEvent.webidl @@ -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; +}; diff --git a/tests/wpt/metadata/html/dom/interfaces.https.html.ini b/tests/wpt/metadata/html/dom/interfaces.https.html.ini index a8713f72d2e..09b0653c697 100644 --- a/tests/wpt/metadata/html/dom/interfaces.https.html.ini +++ b/tests/wpt/metadata/html/dom/interfaces.https.html.ini @@ -10940,36 +10940,6 @@ [ImageData interface: new ImageData(10, 10) must inherit property "data" with the proper type] 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)] expected: FAIL diff --git a/tests/wpt/metadata/html/semantics/forms/form-submission-0/FormDataEvent.window.js.ini b/tests/wpt/metadata/html/semantics/forms/form-submission-0/FormDataEvent.window.js.ini deleted file mode 100644 index 73c78e8da19..00000000000 --- a/tests/wpt/metadata/html/semantics/forms/form-submission-0/FormDataEvent.window.js.ini +++ /dev/null @@ -1,7 +0,0 @@ -[FormDataEvent.window.html] - [Successful FormDataEvent constructor] - expected: FAIL - - [Failing FormDataEvent constructor] - expected: FAIL - diff --git a/tests/wpt/metadata/html/semantics/forms/form-submission-0/constructing-form-data-set.html.ini b/tests/wpt/metadata/html/semantics/forms/form-submission-0/constructing-form-data-set.html.ini index 8916c9b84fd..956dc69251e 100644 --- a/tests/wpt/metadata/html/semantics/forms/form-submission-0/constructing-form-data-set.html.ini +++ b/tests/wpt/metadata/html/semantics/forms/form-submission-0/constructing-form-data-set.html.ini @@ -10,13 +10,3 @@ [Entries added to "formData" IDL attribute should be submitted.] 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 - diff --git a/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini b/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini deleted file mode 100644 index be6f220e3a4..00000000000 --- a/tests/wpt/metadata/html/semantics/forms/form-submission-0/form-submission-algorithm.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[form-submission-algorithm.html] - [If constructing entry list flag of form is true, then return] - expected: FAIL - diff --git a/tests/wpt/metadata/xhr/formdata.htm.ini b/tests/wpt/metadata/xhr/formdata.htm.ini deleted file mode 100644 index cf8cacd24af..00000000000 --- a/tests/wpt/metadata/xhr/formdata.htm.ini +++ /dev/null @@ -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 - diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 86ac13bc878..16cb02a6068 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -27102,7 +27102,7 @@ "testharness" ], "mozilla/interfaces.html": [ - "95ab0109c82c8e90a3e53a3579b9337e2091e26c", + "55cafc9995da83d48230eed5b5c3863d8cf173c9", "testharness" ], "mozilla/interfaces.js": [ diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html index 95ab0109c82..55cafc9995d 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html @@ -77,6 +77,7 @@ test_interfaces([ "FileReader", "FocusEvent", "FormData", + "FormDataEvent", "GainNode", "HashChangeEvent", "Headers", |