diff options
Diffstat (limited to 'components')
-rw-r--r-- | components/script/dom/htmlbuttonelement.rs | 6 | ||||
-rw-r--r-- | components/script/dom/htmlformelement.rs | 232 | ||||
-rw-r--r-- | components/script/dom/htmlinputelement.rs | 6 | ||||
-rw-r--r-- | components/script/dom/webidls/HTMLButtonElement.webidl | 2 | ||||
-rw-r--r-- | components/script/dom/webidls/HTMLInputElement.webidl | 2 |
5 files changed, 227 insertions, 21 deletions
diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs index 356f094f052..07c508bd03d 100644 --- a/components/script/dom/htmlbuttonelement.rs +++ b/components/script/dom/htmlbuttonelement.rs @@ -115,6 +115,12 @@ impl HTMLButtonElementMethods for HTMLButtonElement { // https://html.spec.whatwg.org/multipage/#dom-fs-formtarget make_setter!(SetFormTarget, "formtarget"); + // https://html.spec.whatwg.org/multipage/#attr-fs-formnovalidate + make_bool_getter!(FormNoValidate, "formnovalidate"); + + // https://html.spec.whatwg.org/multipage/#attr-fs-formnovalidate + make_bool_setter!(SetFormNoValidate, "formnovalidate"); + // https://html.spec.whatwg.org/multipage/#dom-fe-name make_getter!(Name, "name"); diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index bccc2912772..28dcdf9cf62 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -9,6 +9,7 @@ use dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElemen use dom::bindings::codegen::Bindings::HTMLFormElementBinding; use dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods; use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; +use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods; use dom::bindings::conversions::DerivedFrom; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; @@ -17,10 +18,13 @@ use dom::bindings::reflector::Reflectable; use dom::document::Document; use dom::element::Element; use dom::event::{Event, EventBubbles, EventCancelable}; -use dom::htmlbuttonelement::{HTMLButtonElement}; +use dom::eventtarget::EventTarget; +use dom::htmlbuttonelement::HTMLButtonElement; use dom::htmldatalistelement::HTMLDataListElement; use dom::htmlelement::HTMLElement; use dom::htmlinputelement::HTMLInputElement; +use dom::htmlobjectelement::HTMLObjectElement; +use dom::htmlselectelement::HTMLSelectElement; use dom::htmltextareaelement::HTMLTextAreaElement; use dom::node::{Node, document_from_node, window_from_node}; use dom::virtualmethods::VirtualMethods; @@ -140,7 +144,7 @@ impl HTMLFormElementMethods for HTMLFormElement { } } -#[derive(Copy, Clone, HeapSizeOf)] +#[derive(Copy, Clone, HeapSizeOf, PartialEq)] pub enum SubmittedFrom { FromFormSubmitMethod, NotFromFormSubmitMethod @@ -154,32 +158,54 @@ pub enum ResetFrom { impl HTMLFormElement { - pub fn submit(&self, _submit_method_flag: SubmittedFrom, submitter: FormSubmitter) { + /// [Form submission](https://html.spec.whatwg.org/multipage/#concept-form-submit) + pub fn submit(&self, submit_method_flag: SubmittedFrom, submitter: FormSubmitter) { // Step 1 let doc = document_from_node(self); let win = window_from_node(self); let base = doc.url(); // TODO: Handle browsing contexts - // TODO: Handle validation - let event = Event::new(GlobalRef::Window(win.r()), - atom!("submit"), - EventBubbles::Bubbles, - EventCancelable::Cancelable); - event.fire(self.upcast()); - if event.DefaultPrevented() { - return; + // Step 4 + if submit_method_flag == SubmittedFrom::NotFromFormSubmitMethod + && !submitter.no_validate(self) + { + if self.interactive_validation().is_err() { + // TODO: Implement event handlers on all form control elements + // XXXKiChjang: We're also calling the following two statements quite often, + // we should refactor it into a function + let event = Event::new(GlobalRef::Window(win.r()), + atom!("invalid"), + EventBubbles::DoesNotBubble, + EventCancelable::NotCancelable); + event.fire(self.upcast()); + return; + } + } + // Step 5 + if submit_method_flag == SubmittedFrom::NotFromFormSubmitMethod { + let event = Event::new(GlobalRef::Window(win.r()), + atom!("submit"), + EventBubbles::Bubbles, + EventCancelable::Cancelable); + event.fire(self.upcast()); + if event.DefaultPrevented() { + return; + } } // Step 6 let form_data = self.get_form_dataset(Some(submitter)); - // Step 7-8 + // Step 7 let mut action = submitter.action(); + // Step 8 if action.is_empty() { action = DOMString::from(base.serialize()); } - // TODO: Resolve the url relative to the submitter element - // Step 10-15 - let action_components = - UrlParser::new().base_url(base).parse(&action).unwrap_or((*base).clone()); + // Step 9-11 + let action_components = match UrlParser::new().base_url(base).parse(&action) { + Ok(url) => url, + Err(_) => return + }; + // Step 12-15 let _action = action_components.serialize(); let scheme = action_components.scheme.clone(); let enctype = submitter.enctype(); @@ -219,13 +245,123 @@ impl HTMLFormElement { win.pipeline(), load_data)).unwrap(); } + /// Interactively validate the constraints of form elements + /// https://html.spec.whatwg.org/multipage/#interactively-validate-the-constraints + fn interactive_validation(&self) -> Result<(), ()> { + // Step 1-3 + let unhandled_invalid_controls = match self.static_validation() { + Ok(()) => return Ok(()), + Err(err) => err + }; + // TODO: Report the problems with the constraints of at least one of + // the elements given in unhandled invalid controls to the user + // Step 4 + Err(()) + } + + /// Statitically validate the constraints of form elements + /// https://html.spec.whatwg.org/multipage/#statically-validate-the-constraints + fn static_validation(&self) -> Result<(), Vec<FormSubmittableElement>> { + let node = self.upcast::<Node>(); + // FIXME(#3553): This is an incorrect way of getting controls owned by the + // form, refactor this when html5ever's form owner PR lands + // Step 1-3 + let invalid_controls = node.traverse_preorder().filter_map(|field| { + if let Some(el) = field.downcast::<Element>() { + None // Remove this line if you decide to refactor + + // XXXKiChjang: Refactor the following commented up code so that form control elements + // each have a candidate_for_validation and satisfies_constraints methods + + // XXXKiChjang: This should go into candidate_for_validation + + // if field.ancestors() + // .any(|a| Root::downcast::<HTMLDataListElement>(a).is_some()) + // // XXXKiChjang this may be wrong, this is not checking the ancestor + // // elements to find whether an HTMLFieldSetElement exists and is disabled + // || el.get_disabled_state() { + // return None; + // } + + + // XXXKiChjang: This should go into satisfies_constraints for each individual form element + + // match field.type_id() { + // NodeTypeId::Element(ElementTypeId::HTMLElement(element)) => { + // match element { + // HTMLElementTypeId::HTMLButtonElement => { + // let button = field.downcast::<HTMLButtonElement>().unwrap(); + // // Substep 1 + // // https://html.spec.whatwg.org/multipage/#the-button-element:barred-from-constraint-validation + // if button.Type() != "submit" { return None; } + // // Substep 2 + // // TODO: Check constraints on HTMLButtonElement + // // Substep 3 + // Some(FormSubmittableElement::ButtonElement(Root::from_ref(&*button))) + // } + // HTMLElementTypeId::HTMLInputElement => { + // let input = field.downcast::<HTMLInputElement>().unwrap(); + // // Substep 1 + // // https://html.spec.whatwg.org/multipage/#candidate-for-constraint-validation + // if input.type_() == atom!("hidden") + // || input.type_() == atom!("reset") + // || input.type_() == atom!("button") + // || input.ReadOnly() { return None; } + // // Substep 2 + // // TODO: Check constraints on HTMLInputElement + // // Substep 3 + // Some(FormSubmittableElement::InputElement(Root::from_ref(&*input))) + // } + // HTMLElementTypeId::HTMLSelectElement => { + // let select = field.downcast::<HTMLSelectElement>().unwrap(); + // // Substep 1 not necessary, HTMLSelectElements are not barred from constraint validation + // // Substep 2 + // // TODO: Check constraints on HTMLSelectElement + // // Substep 3 + // Some(FormSubmittableElement::SelectElement(Root::from_ref(&*select))) + // } + // HTMLElementTypeId::HTMLTextAreaElement => { + // let textarea = field.downcast::<HTMLTextAreaElement>().unwrap(); + // // Substep 1 + // // https://html.spec.whatwg.org/multipage/#the-textarea-element:barred-from-constraint-validation + // if textarea.ReadOnly() { return None; } + // // Substep 2 + // // TODO: Check constraints on HTMLTextAreaElement + // // Substep 3 + // Some(FormSubmittableElement::TextAreaElement(Root::from_ref(&*textarea))) + // } + // _ => None + // } + // } + // _ => None + // } + } else { + None + } + }).collect::<Vec<FormSubmittableElement>>(); + // Step 4 + if invalid_controls.is_empty() { return Ok(()); } + // Step 5-6 + let win = window_from_node(self); + let unhandled_invalid_controls = invalid_controls.into_iter().filter_map(|field| { + let event = Event::new(GlobalRef::Window(win.r()), + atom!("invalid"), + EventBubbles::DoesNotBubble, + EventCancelable::Cancelable); + event.fire(field.as_event_target()); + if !event.DefaultPrevented() { return Some(field); } + None + }).collect::<Vec<FormSubmittableElement>>(); + // Step 7 + Err(unhandled_invalid_controls) + } + /// https://html.spec.whatwg.org/multipage/#constructing-the-form-data-set /// Steps range from 1 to 3 fn get_unclean_dataset(&self, submitter: Option<FormSubmitter>) -> Vec<FormDatum> { let node = self.upcast::<Node>(); - // TODO: This is an incorrect way of getting controls owned - // by the form, but good enough until html5ever lands - // Step 1-2 + // FIXME(#3553): This is an incorrect way of getting controls owned + // by the form, but good enough until html5ever lands node.traverse_preorder().filter_map(|child| { // Step 3.1: The field element is disabled. match child.downcast::<Element>() { @@ -382,6 +518,29 @@ pub enum FormMethod { FormDialog } +#[derive(HeapSizeOf)] +pub enum FormSubmittableElement { + ButtonElement(Root<HTMLButtonElement>), + InputElement(Root<HTMLInputElement>), + // TODO: HTMLKeygenElement unimplemented + // KeygenElement(&'a HTMLKeygenElement), + ObjectElement(Root<HTMLObjectElement>), + SelectElement(Root<HTMLSelectElement>), + TextAreaElement(Root<HTMLTextAreaElement>) +} + +impl FormSubmittableElement { + fn as_event_target(&self) -> &EventTarget { + match *self { + FormSubmittableElement::ButtonElement(ref button) => button.r().upcast(), + FormSubmittableElement::InputElement(ref input) => input.r().upcast(), + FormSubmittableElement::ObjectElement(ref object) => object.r().upcast(), + FormSubmittableElement::SelectElement(ref select) => select.r().upcast(), + FormSubmittableElement::TextAreaElement(ref textarea) => textarea.r().upcast() + } + } +} + #[derive(Copy, Clone, HeapSizeOf)] pub enum FormSubmitter<'a> { FormElement(&'a HTMLFormElement), @@ -466,6 +625,22 @@ impl<'a> FormSubmitter<'a> { } } } + + fn no_validate(&self, form_owner: &HTMLFormElement) -> bool { + match *self { + FormSubmitter::FormElement(form) => form.NoValidate(), + FormSubmitter::InputElement(input_element) => { + input_element.get_form_boolean_attribute(&atom!("formnovalidate"), + |i| i.FormNoValidate(), + |f| f.NoValidate()) + } + FormSubmitter::ButtonElement(button_element) => { + button_element.get_form_boolean_attribute(&atom!("formnovalidate"), + |i| i.FormNoValidate(), + |f| f.NoValidate()) + } + } + } } pub trait FormControl: DerivedFrom<Element> + Reflectable { @@ -506,9 +681,28 @@ pub trait FormControl: DerivedFrom<Element> + Reflectable { } } + fn get_form_boolean_attribute<InputFn, OwnerFn>(&self, + attr: &Atom, + input: InputFn, + owner: OwnerFn) + -> bool + where InputFn: Fn(&Self) -> bool, + OwnerFn: Fn(&HTMLFormElement) -> bool + { + if self.to_element().has_attribute(attr) { + input(self) + } else { + self.form_owner().map_or(false, |t| owner(t.r())) + } + } + fn to_element(&self) -> &Element { self.upcast() } + + // XXXKiChjang: Implement these on inheritors + // fn candidate_for_validation(&self) -> bool; + // fn satisfies_constraints(&self) -> bool; } impl VirtualMethods for HTMLFormElement { diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 092e963840b..35d04fed47e 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -340,6 +340,12 @@ impl HTMLInputElementMethods for HTMLInputElement { // https://html.spec.whatwg.org/multipage/#dom-input-formtarget make_setter!(SetFormTarget, "formtarget"); + // https://html.spec.whatwg.org/multipage/#attr-fs-formnovalidate + make_bool_getter!(FormNoValidate, "formnovalidate"); + + // https://html.spec.whatwg.org/multipage/#attr-fs-formnovalidate + make_bool_setter!(SetFormNoValidate, "formnovalidate"); + // https://html.spec.whatwg.org/multipage/#dom-input-maxlength make_int_getter!(MaxLength, "maxlength", DEFAULT_MAX_LENGTH); diff --git a/components/script/dom/webidls/HTMLButtonElement.webidl b/components/script/dom/webidls/HTMLButtonElement.webidl index 56ee8de8d74..f928031d5cd 100644 --- a/components/script/dom/webidls/HTMLButtonElement.webidl +++ b/components/script/dom/webidls/HTMLButtonElement.webidl @@ -11,7 +11,7 @@ interface HTMLButtonElement : HTMLElement { attribute DOMString formAction; attribute DOMString formEnctype; attribute DOMString formMethod; - // attribute boolean formNoValidate; + attribute boolean formNoValidate; attribute DOMString formTarget; attribute DOMString name; attribute DOMString type; diff --git a/components/script/dom/webidls/HTMLInputElement.webidl b/components/script/dom/webidls/HTMLInputElement.webidl index 274a10ce957..8cc0fa66ae7 100644 --- a/components/script/dom/webidls/HTMLInputElement.webidl +++ b/components/script/dom/webidls/HTMLInputElement.webidl @@ -18,7 +18,7 @@ interface HTMLInputElement : HTMLElement { attribute DOMString formAction; attribute DOMString formEnctype; attribute DOMString formMethod; - // attribute boolean formNoValidate; + attribute boolean formNoValidate; attribute DOMString formTarget; // attribute unsigned long height; attribute boolean indeterminate; |