aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
Diffstat (limited to 'components/script')
-rw-r--r--components/script/dom/htmlbuttonelement.rs6
-rw-r--r--components/script/dom/htmlformelement.rs232
-rw-r--r--components/script/dom/htmlinputelement.rs6
-rw-r--r--components/script/dom/webidls/HTMLButtonElement.webidl2
-rw-r--r--components/script/dom/webidls/HTMLInputElement.webidl2
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;