Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement formdata event #22660

Merged
merged 3 commits into from Jan 22, 2019
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -30,6 +30,7 @@ error
fantasy
fetch
file
formdata
fullscreenchange
fullscreenerror
gattserverdisconnected
@@ -3235,6 +3235,18 @@ impl Element {
let root = node.GetRootNode();
root.is::<Document>()
}

// https://html.spec.whatwg.org/multipage/#cannot-navigate
pub fn cannot_navigate(&self) -> bool {
let document = document_from_node(self);

// Step 1.
!document.is_fully_active() ||
(
// Step 2.
!self.is::<HTMLAnchorElement>() && !self.is_connected()
)
}
}

impl Element {
@@ -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))
}
}

@@ -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()
}
}
@@ -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()),
@@ -312,50 +318,73 @@ 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
if self.upcast::<Element>().cannot_navigate() {
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
self.upcast::<EventTarget>().fire_event(atom!("invalid"));
return;
}
}
// Step 5
// Step 7
if submit_method_flag == SubmittedFrom::NotFromForm {
let event = self
.upcast::<EventTarget>()
.fire_bubbling_cancelable_event(atom!("submit"));
if event.DefaultPrevented() {
return;
}

// Step 7-3
if self.upcast::<Element>().cannot_navigate() {
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,
};

// Step 10
if self.upcast::<Element>().cannot_navigate() {
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 +404,7 @@ impl HTMLFormElement {
Some(target_document.url()),
);

// Step 18
// Step 22
match (&*scheme, method) {
(_, FormMethod::FormDialog) => {
// TODO: Submit dialog
@@ -597,18 +626,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 +686,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 +718,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 +740,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) {
@@ -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;
@@ -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;
This conversation was marked as resolved by CYBAI

This comment has been minimized.

@jdm

jdm Jan 9, 2019

Member

What happens if we uncomment this? If there's an error, do we have an issue filed for it, and can we add a comment?

This comment has been minimized.

@CYBAI

CYBAI Jan 10, 2019

Author Collaborator

It will fail with this compilation error. I didn't find a corresponding issue. I'll try to read the webidl codegen to see what the root cause is later.

error[E0599]: no function or associated item named `empty` found for type `dom::bindings::codegen::Bindings::FormDataEventBinding::FormDataEventInit` in the current scope
   --> /Users/cybai/codespace/mozilla/servo/target/debug/build/script-59d4418593395066/out/Bindings/FormDataEventBinding.rs:857:95
    |
259 | pub struct FormDataEventInit {
    | ---------------------------- function or associated item `empty` not found for this
...
857 |             crate::dom::bindings::codegen::Bindings::FormDataEventBinding::FormDataEventInit::empty()
    |             ----------------------------------------------------------------------------------^^^^^
    |             |
    |             function or associated item not found in `dom::bindings::codegen::Bindings::FormDataEventBinding::FormDataEventInit`

This comment has been minimized.

@jdm

jdm Jan 10, 2019

Member

Ok, it is clear to me why this happens now. Filed #22670 about it.

This comment has been minimized.

@CYBAI

CYBAI Jan 10, 2019

Author Collaborator

Thanks a lot! After reading the issue, I can understand why it failed to compile with required :)

};
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.