From 3ed6e7f5382e7080edd3a1160686649d59db3828 Mon Sep 17 00:00:00 2001 From: Arpit Tyagi Date: Mon, 25 Apr 2016 19:00:20 -0400 Subject: [PATCH] HTML 5 Form validations Fixed the compilation issues in h5fv. Still need to incorporate the other half of keith's recommended changes Moved the code to include check and report validity to webidl file Fixed focus issue in htmlformelement, removed warning code Fixed tidy-test issues moved the validation logic from validity state to individual form elements ike inputelement, selectelement --- components/script/dom/htmlbuttonelement.rs | 55 +++- components/script/dom/htmlfieldsetelement.rs | 49 +++- components/script/dom/htmlformelement.rs | 95 ++++++- components/script/dom/htmlinputelement.rs | 245 +++++++++++++++++- components/script/dom/htmllabelelement.rs | 51 +++- components/script/dom/htmllegendelement.rs | 49 +++- components/script/dom/htmlobjectelement.rs | 55 +++- components/script/dom/htmloutputelement.rs | 49 +++- components/script/dom/htmlselectelement.rs | 95 ++++++- components/script/dom/htmltextareaelement.rs | 127 ++++++++- components/script/dom/validation.rs | 5 +- components/script/dom/validitystate.rs | 161 +++++++++--- .../script/dom/webidls/HTMLFormElement.webidl | 6 +- 13 files changed, 976 insertions(+), 66 deletions(-) diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs index 22fed30a2df6..aed274660e42 100644 --- a/components/script/dom/htmlbuttonelement.rs +++ b/components/script/dom/htmlbuttonelement.rs @@ -6,6 +6,7 @@ use dom::activation::{Activatable, ActivationSource, synthetic_click_activation} use dom::attr::Attr; use dom::bindings::codegen::Bindings::HTMLButtonElementBinding; use dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods; +use dom::bindings::codegen::Bindings::ValidityStateBinding::ValidityStateMethods; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::document::Document; @@ -27,6 +28,7 @@ use string_cache::Atom; use style::element_state::*; use util::str::DOMString; + #[derive(JSTraceable, PartialEq, Copy, Clone)] #[derive(HeapSizeOf)] enum ButtonType { @@ -202,9 +204,58 @@ impl VirtualMethods for HTMLButtonElement { } } -impl FormControl for HTMLButtonElement {} +impl FormControl for HTMLButtonElement { + fn candidate_for_validation(&self, element: &Element) -> bool { + if element.as_maybe_validatable().is_some() { + return true + } + else { + return false + } + } + + fn satisfies_constraints(&self, element: &Element) -> bool { + let vs = ValidityState::new(window_from_node(self).r(), element); + return vs.Valid() + } + fn ValueMissing(&self) -> bool { + return false; + } + fn TypeMismatch(&self) -> bool { + return false; + } + fn PatternMismatch(&self) -> bool { + return false; + } + fn TooLong(&self) -> bool { + return false; + } + fn TooShort(&self) -> bool { + return false; + } + fn RangeUnderflow(&self) -> bool { + return false; + } + fn RangeOverflow(&self) -> bool { + return false; + } + fn StepMismatch(&self) -> bool { + return false; + } + fn BadInput(&self) -> bool { + return false; + } + fn CustomError(&self) -> bool { + return false; + } + +} -impl Validatable for HTMLButtonElement {} +impl Validatable for HTMLButtonElement { + fn get_value_for_validation(&self) -> Option{ + None + } +} impl Activatable for HTMLButtonElement { fn as_element(&self) -> &Element { diff --git a/components/script/dom/htmlfieldsetelement.rs b/components/script/dom/htmlfieldsetelement.rs index b9800dcd1f1f..693629ef572c 100644 --- a/components/script/dom/htmlfieldsetelement.rs +++ b/components/script/dom/htmlfieldsetelement.rs @@ -5,6 +5,7 @@ use dom::attr::Attr; use dom::bindings::codegen::Bindings::HTMLFieldSetElementBinding; use dom::bindings::codegen::Bindings::HTMLFieldSetElementBinding::HTMLFieldSetElementMethods; +use dom::bindings::codegen::Bindings::ValidityStateBinding::ValidityStateMethods; use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; use dom::bindings::js::{Root, RootedReference}; use dom::document::Document; @@ -20,6 +21,7 @@ use string_cache::Atom; use style::element_state::*; use util::str::DOMString; + #[dom_struct] pub struct HTMLFieldSetElement { htmlelement: HTMLElement @@ -151,4 +153,49 @@ impl VirtualMethods for HTMLFieldSetElement { } } -impl FormControl for HTMLFieldSetElement {} +impl FormControl for HTMLFieldSetElement { + fn candidate_for_validation(&self, element: &Element) -> bool { + if element.as_maybe_validatable().is_some() { + return true + } + else { + return false + } + } + + fn satisfies_constraints(&self, element: &Element) -> bool { + let vs = ValidityState::new(window_from_node(self).r(), element); + return vs.Valid() + } + fn ValueMissing(&self) -> bool { + return false; + } + fn TypeMismatch(&self) -> bool { + return false; + } + fn PatternMismatch(&self) -> bool { + return false; + } + fn TooLong(&self) -> bool { + return false; + } + fn TooShort(&self) -> bool { + return false; + } + fn RangeUnderflow(&self) -> bool { + return false; + } + fn RangeOverflow(&self) -> bool { + return false; + } + fn StepMismatch(&self) -> bool { + return false; + } + fn BadInput(&self) -> bool { + return false; + } + fn CustomError(&self) -> bool { + return false; + } + +} diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index 0e8b6174ab50..a0260a1dee8c 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -7,6 +7,7 @@ use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods; +use dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods; use dom::bindings::codegen::Bindings::HTMLFormElementBinding; use dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods; use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; @@ -228,6 +229,24 @@ impl HTMLFormElementMethods for HTMLFormElement { fn Length(&self) -> u32 { self.Elements().Length() as u32 } + // https://html.spec.whatwg.org/multipage/#the-form-element:statically-validate-the-constraints + fn Check_validity(&self) -> bool { + let _unhandled_invalid_controls = match self.static_validation() { + Ok(()) => return true, + Err(err) => { + println!("Error in form fields in CheckValdity"); + return false + } + }; + } + // https://html.spec.whatwg.org/multipage/#the-form-element:interactively-validate-the-constraints + fn Report_validity(&self) -> bool { + if self.interactive_validation().is_err() { + return false; + } else { + return true; + } + } } #[derive(Copy, Clone, HeapSizeOf, PartialEq)] @@ -446,14 +465,16 @@ impl HTMLFormElement { // Step 1-3 let _unhandled_invalid_controls = match self.static_validation() { Ok(()) => return Ok(()), - Err(err) => err + Err(err) => { + err[0].as_event_target().downcast::().unwrap().Focus(); + 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> { @@ -463,8 +484,53 @@ impl HTMLFormElement { // Step 1-3 let invalid_controls = node.traverse_preorder().filter_map(|field| { if let Some(_el) = field.downcast::() { - None // Remove this line if you decide to refactor - + //if self.check_if_candidate_for_validation(_el) & !self.check_if_candidate_satisfies_constraints(_el) { + match _el.upcast::().type_id() { + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) => { + let html_input_element = _el.downcast::().unwrap(); + if html_input_element.candidate_for_validation(_el) + & !html_input_element.satisfies_constraints(_el) { + return Some(FormSubmittableElement::InputElement( + Root::from_ref(_el.downcast::().unwrap()))) + } + } + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) => { + let html_button_element = _el.downcast::().unwrap(); + if html_button_element.candidate_for_validation(_el) + & !html_button_element.satisfies_constraints(_el) { + return Some(FormSubmittableElement::ButtonElement( + Root::from_ref(_el.downcast::().unwrap()))) + } + } + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) => { + let html_select_element = _el.downcast::().unwrap(); + if html_select_element.candidate_for_validation(_el) + & !html_select_element.satisfies_constraints(_el) { + return Some(FormSubmittableElement::SelectElement( + Root::from_ref(_el.downcast::().unwrap()))) + } + } + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => { + let html_textarea_element = _el.downcast::().unwrap(); + if html_textarea_element.candidate_for_validation(_el) + & !html_textarea_element.satisfies_constraints(_el) { + return Some(FormSubmittableElement::TextAreaElement( + Root::from_ref(_el.downcast::().unwrap()))) + } + } + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement)) => { + let html_object_element = _el.downcast::().unwrap(); + if html_object_element.candidate_for_validation(_el) + & !html_object_element.satisfies_constraints(_el) { + return Some(FormSubmittableElement::ObjectElement( + Root::from_ref(_el.downcast::().unwrap()))) + } + } + _ => { } + } + //} + // None // Remove this line if you decide to refactor + None // XXXKiChjang: Form control elements should each have a candidate_for_validation // and satisfies_constraints methods @@ -473,7 +539,10 @@ impl HTMLFormElement { } }).collect::>(); // Step 4 - if invalid_controls.is_empty() { return Ok(()); } + if invalid_controls.is_empty() { + println!("Invalid Controls is Empty"); + return Ok(()); + } // Step 5-6 let unhandled_invalid_controls = invalid_controls.into_iter().filter_map(|field| { let event = field.as_event_target() @@ -858,8 +927,20 @@ pub trait FormControl: DerivedFrom + Reflectable { } // XXXKiChjang: Implement these on inheritors - // fn candidate_for_validation(&self) -> bool; - // fn satisfies_constraints(&self) -> bool; + fn candidate_for_validation(&self, element: &Element) -> bool; + fn satisfies_constraints(&self, element: &Element) -> bool; + // Implement these on htmlinputelement, htmlselectelement, etc + fn ValueMissing(&self) -> bool; + fn TypeMismatch(&self) -> bool; + fn PatternMismatch(&self) -> bool; + fn TooLong(&self) -> bool; + fn TooShort(&self) -> bool; + fn RangeUnderflow(&self) -> bool; + fn RangeOverflow(&self) -> bool; + fn StepMismatch(&self) -> bool; + fn BadInput(&self) -> bool; + fn CustomError(&self) -> bool; + } impl VirtualMethods for HTMLFormElement { diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 0e675b35b27d..69ee4111e830 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -11,6 +11,7 @@ use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::HTMLInputElementBinding; use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods; use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods; +use dom::bindings::codegen::Bindings::ValidityStateBinding::ValidityStateMethods; use dom::bindings::error::{Error, ErrorResult}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; @@ -29,8 +30,11 @@ use dom::node::{Node, NodeDamage, UnbindContext}; use dom::node::{document_from_node, window_from_node}; use dom::nodelist::NodeList; use dom::validation::Validatable; +use dom::validitystate::ValidityState; use dom::virtualmethods::VirtualMethods; use msg::constellation_msg::ConstellationChan; +use regex::Regex; +//use range::Range; use script_runtime::CommonScriptMsg; use script_runtime::ScriptThreadEventCategory::InputEvent; use script_thread::Runnable; @@ -86,7 +90,6 @@ pub struct HTMLInputElement { activation_state: DOMRefCell, // https://html.spec.whatwg.org/multipage/#concept-input-value-dirty-flag value_dirty: Cell, - // TODO: selected files for file input } @@ -936,9 +939,245 @@ impl VirtualMethods for HTMLInputElement { } } -impl FormControl for HTMLInputElement {} +impl FormControl for HTMLInputElement { + fn candidate_for_validation(&self, element: &Element) -> bool { + if element.as_maybe_validatable().is_some() { + return true + } + else { + return false + } + } + + fn satisfies_constraints(&self, element: &Element) -> bool { + let vs = ValidityState::new(window_from_node(self).r(), element); + return vs.Valid() + } + + fn ValueMissing(&self) -> bool { + let attr_value_check = self.upcast::().get_attribute_by_name(DOMString::from("required")) + .map(|s| s.Value()); + if attr_value_check.is_some() { + //let html_input_element = self.element.downcast::().unwrap(); + let input_value_check = self.get_value_for_validation(); + if input_value_check.is_some() { + return false; + } + else { + println!("Error - Value missing in html input element"); + return true; + } + } + else { + return false; + } + } + fn TypeMismatch(&self) -> bool { + //let regex_email: Regex = Regex::new(r"/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-] \ + // {0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/").unwrap(); + let regex_email: Regex = Regex::new(r"").unwrap(); + let regex_url: Regex = Regex::new(r"").unwrap(); + let regex_number: Regex = Regex::new(r"").unwrap(); + let attr_value_check = self.upcast::().get_attribute_by_name(DOMString::from("type")) + .map(|s| s.Value()); + match attr_value_check { + Some(attr_value) => { + // let html_input_element = self.element.downcast::().unwrap(); + let input_value_check = self.get_value_for_validation(); + match input_value_check { + Some(input_value) => { + if (attr_value == "email") & (!regex_email.is_match(&*input_value)) { + println!("Type error in html text input [email]"); + return true; + } + else if (attr_value == "url") & (!regex_url.is_match(&*input_value)) { + println!("Type error in html text input [url]"); + return true; + } + else if (attr_value == "number") & (!regex_number.is_match(&*input_value)) { + println!("Type error in html text input [number]"); + return true; + } + else { + return false; + } + }, + None => { + return false; + } + } + }, + None => { + return false; + } + } + //let data = element1.form_datum(Some(FormSubmitter::InputElement(element1))); + } + fn PatternMismatch(&self) -> bool { + return false; + } + fn TooLong(&self) -> bool { + + let attr_value_check = self.upcast::().get_attribute_by_name(DOMString::from("maxlength")) + .map(|s| s.Value()); + match attr_value_check { + Some(attr_value) => { + let maxlength = attr_value.parse().unwrap(); + //let html_input_element = self.element.downcast::().unwrap(); + let input_value_check = self.get_value_for_validation(); + match input_value_check { + Some(input_value) => { + if input_value.len() > maxlength { + println!("Error - TooLong html input element"); + return true; + } + else { + return false; + } + }, + None => { + return false; + } + } + }, + None => { + return false; + } + } + } + fn TooShort(&self) -> bool { + let attr_value_check = self.upcast::().get_attribute_by_name(DOMString::from("minlength")) + .map(|s| s.Value()); + match attr_value_check { + Some(attr_value) => { + let minlength = attr_value.parse().unwrap(); + // let html_input_element = self.element.downcast::().unwrap(); + let input_value_check = self.get_value_for_validation(); + match input_value_check { + Some(input_value) => { + if input_value.len() < minlength { + println!("Error - TooShort html input element"); + return true; + } + else { + return false; + } + }, + None => { + return false; + } + } + }, + None => { + return false; + } + } + } + fn RangeUnderflow(&self) -> bool { + let attr_value_check = self.upcast::().get_attribute_by_name(DOMString::from("min")) + .map(|s| s.Value()); + match attr_value_check { + Some(attr_value) => { + let min: f32 = attr_value.parse().unwrap(); + // let html_input_element = self.element.downcast::().unwrap(); + let input_value_check = self.get_value_for_validation(); + match input_value_check { + Some(input_value) => { + let text_value: f32 = input_value.parse().unwrap(); + if text_value < min { + println!("Error - RangeUnderflow html input element"); + return true; + } + else { + return false; + } + }, + None => { + return false; + } + } + }, + None => { + return false; + } + } + } + fn RangeOverflow(&self) -> bool { + let attr_value_check = self.upcast::().get_attribute_by_name(DOMString::from("max")) + .map(|s| s.Value()); + match attr_value_check { + Some(attr_value) => { + let max: f32 = attr_value.parse().unwrap(); + // let html_input_element = self.element.downcast::().unwrap(); + let input_value_check = self.get_value_for_validation(); + match input_value_check { + Some(input_value) => { + let text_value: f32 = input_value.parse().unwrap(); + if text_value > max { + println!("Error - RangeOverflow html input element"); + return true; + } + else { + return false; + } + }, + None => { + return false; + } + } + }, + None => { + return false; + } + } + } + fn StepMismatch(&self) -> bool { + + let attr_value_check = self.upcast::().get_attribute_by_name(DOMString::from("step")) + .map(|s| s.Value()); + match attr_value_check { + Some(attr_value) => { + let step: f32 = attr_value.parse().unwrap(); + // let html_input_element = self.element.downcast::().unwrap(); + let input_value_check = self.get_value_for_validation(); + match input_value_check { + Some(input_value) => { + let text_value: f32 = input_value.parse().unwrap(); + if text_value % step == 0.0_f32 { + return false; + } + else { + println!("Error - StepMismatch html input element"); + return true; + } + }, + None => { + return false; + } + } + }, + None => { + return false; + } + } + } + fn BadInput(&self) -> bool { + return false; + } + fn CustomError(&self) -> bool { + return false; + } -impl Validatable for HTMLInputElement {} +} +impl Validatable for HTMLInputElement { + fn get_value_for_validation(&self) -> Option{ + if self.Value().is_empty() { + None + } else { + Some(self.Value()) + } + } +} impl Activatable for HTMLInputElement { fn as_element(&self) -> &Element { diff --git a/components/script/dom/htmllabelelement.rs b/components/script/dom/htmllabelelement.rs index 90d4a16b86e1..fb14a84b44d8 100644 --- a/components/script/dom/htmllabelelement.rs +++ b/components/script/dom/htmllabelelement.rs @@ -6,6 +6,7 @@ use dom::activation::{Activatable, ActivationSource, synthetic_click_activation} use dom::attr::AttrValue; use dom::bindings::codegen::Bindings::HTMLLabelElementBinding; use dom::bindings::codegen::Bindings::HTMLLabelElementBinding::HTMLLabelElementMethods; +use dom::bindings::codegen::Bindings::ValidityStateBinding::ValidityStateMethods; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::document::Document; @@ -15,10 +16,14 @@ use dom::eventtarget::EventTarget; use dom::htmlelement::HTMLElement; use dom::htmlformelement::{FormControl, HTMLFormElement}; use dom::node::{document_from_node, Node}; +use dom::node::{window_from_node}; +use dom::validitystate::ValidityState; use dom::virtualmethods::VirtualMethods; use string_cache::Atom; use util::str::DOMString; + + #[dom_struct] pub struct HTMLLabelElement { htmlelement: HTMLElement, @@ -138,4 +143,48 @@ impl HTMLLabelElement { } } -impl FormControl for HTMLLabelElement {} +impl FormControl for HTMLLabelElement { + fn candidate_for_validation(&self, element: &Element) -> bool { + if element.as_maybe_validatable().is_some() { + return true + } + else { + return false + } + } + + fn satisfies_constraints(&self, element: &Element) -> bool { + let vs = ValidityState::new(window_from_node(self).r(), element); + return vs.Valid() + } + fn ValueMissing(&self) -> bool { + return false; + } + fn TypeMismatch(&self) -> bool { + return false; + } + fn PatternMismatch(&self) -> bool { + return false; + } + fn TooLong(&self) -> bool { + return false; + } + fn TooShort(&self) -> bool { + return false; + } + fn RangeUnderflow(&self) -> bool { + return false; + } + fn RangeOverflow(&self) -> bool { + return false; + } + fn StepMismatch(&self) -> bool { + return false; + } + fn BadInput(&self) -> bool { + return false; + } + fn CustomError(&self) -> bool { + return false; + } +} diff --git a/components/script/dom/htmllegendelement.rs b/components/script/dom/htmllegendelement.rs index d0b3487113a3..50e2567e5b3e 100644 --- a/components/script/dom/htmllegendelement.rs +++ b/components/script/dom/htmllegendelement.rs @@ -5,6 +5,7 @@ use dom::bindings::codegen::Bindings::HTMLLegendElementBinding; use dom::bindings::codegen::Bindings::HTMLLegendElementBinding::HTMLLegendElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; +use dom::bindings::codegen::Bindings::ValidityStateBinding::ValidityStateMethods; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::document::Document; @@ -12,7 +13,9 @@ use dom::element::Element; use dom::htmlelement::HTMLElement; use dom::htmlfieldsetelement::HTMLFieldSetElement; use dom::htmlformelement::{HTMLFormElement, FormControl}; +use dom::node::window_from_node; use dom::node::{Node, UnbindContext}; +use dom::validitystate::ValidityState; use dom::virtualmethods::VirtualMethods; use string_cache::Atom; use util::str::DOMString; @@ -81,4 +84,48 @@ impl HTMLLegendElementMethods for HTMLLegendElement { } } -impl FormControl for HTMLLegendElement {} +impl FormControl for HTMLLegendElement { + fn candidate_for_validation(&self, element: &Element) -> bool { + if element.as_maybe_validatable().is_some() { + return true + } + else { + return false + } + } + + fn satisfies_constraints(&self, element: &Element) -> bool { + let vs = ValidityState::new(window_from_node(self).r(), element); + return vs.Valid() + } + fn ValueMissing(&self) -> bool { + return false; + } + fn TypeMismatch(&self) -> bool { + return false; + } + fn PatternMismatch(&self) -> bool { + return false; + } + fn TooLong(&self) -> bool { + return false; + } + fn TooShort(&self) -> bool { + return false; + } + fn RangeUnderflow(&self) -> bool { + return false; + } + fn RangeOverflow(&self) -> bool { + return false; + } + fn StepMismatch(&self) -> bool { + return false; + } + fn BadInput(&self) -> bool { + return false; + } + fn CustomError(&self) -> bool { + return false; + } +} diff --git a/components/script/dom/htmlobjectelement.rs b/components/script/dom/htmlobjectelement.rs index 4d2b1cde7783..53f98b84e7b3 100644 --- a/components/script/dom/htmlobjectelement.rs +++ b/components/script/dom/htmlobjectelement.rs @@ -6,6 +6,7 @@ use dom::attr::Attr; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::HTMLObjectElementBinding; use dom::bindings::codegen::Bindings::HTMLObjectElementBinding::HTMLObjectElementMethods; +use dom::bindings::codegen::Bindings::ValidityStateBinding::ValidityStateMethods; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::document::Document; @@ -21,6 +22,7 @@ use std::sync::Arc; use string_cache::Atom; use util::str::DOMString; + #[dom_struct] pub struct HTMLObjectElement { htmlelement: HTMLElement, @@ -92,8 +94,11 @@ impl HTMLObjectElementMethods for HTMLObjectElement { } } -impl Validatable for HTMLObjectElement {} - +impl Validatable for HTMLObjectElement { + fn get_value_for_validation(&self) -> Option{ + None + } +} impl VirtualMethods for HTMLObjectElement { fn super_type(&self) -> Option<&VirtualMethods> { Some(self.upcast::() as &VirtualMethods) @@ -112,4 +117,48 @@ impl VirtualMethods for HTMLObjectElement { } } -impl FormControl for HTMLObjectElement {} +impl FormControl for HTMLObjectElement { + fn candidate_for_validation(&self, element: &Element) -> bool { + if element.as_maybe_validatable().is_some() { + return true + } + else { + return false + } + } + + fn satisfies_constraints(&self, element: &Element) -> bool { + let vs = ValidityState::new(window_from_node(self).r(), element); + return vs.Valid() + } + fn ValueMissing(&self) -> bool { + return false; + } + fn TypeMismatch(&self) -> bool { + return false; + } + fn PatternMismatch(&self) -> bool { + return false; + } + fn TooLong(&self) -> bool { + return false; + } + fn TooShort(&self) -> bool { + return false; + } + fn RangeUnderflow(&self) -> bool { + return false; + } + fn RangeOverflow(&self) -> bool { + return false; + } + fn StepMismatch(&self) -> bool { + return false; + } + fn BadInput(&self) -> bool { + return false; + } + fn CustomError(&self) -> bool { + return false; + } +} diff --git a/components/script/dom/htmloutputelement.rs b/components/script/dom/htmloutputelement.rs index b614684b863d..955e4f2062d9 100644 --- a/components/script/dom/htmloutputelement.rs +++ b/components/script/dom/htmloutputelement.rs @@ -4,9 +4,11 @@ use dom::bindings::codegen::Bindings::HTMLOutputElementBinding; use dom::bindings::codegen::Bindings::HTMLOutputElementBinding::HTMLOutputElementMethods; +use dom::bindings::codegen::Bindings::ValidityStateBinding::ValidityStateMethods; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::document::Document; +use dom::element::Element; use dom::htmlelement::HTMLElement; use dom::htmlformelement::{FormControl, HTMLFormElement}; use dom::node::{Node, window_from_node}; @@ -15,6 +17,7 @@ use dom::validitystate::ValidityState; use string_cache::Atom; use util::str::DOMString; + #[dom_struct] pub struct HTMLOutputElement { htmlelement: HTMLElement @@ -57,4 +60,48 @@ impl HTMLOutputElementMethods for HTMLOutputElement { } } -impl FormControl for HTMLOutputElement {} +impl FormControl for HTMLOutputElement { + fn candidate_for_validation(&self, element: &Element) -> bool { + if element.as_maybe_validatable().is_some() { + return true + } + else { + return false + } + } + + fn satisfies_constraints(&self, element: &Element) -> bool { + let vs = ValidityState::new(window_from_node(self).r(), element); + return vs.Valid() + } + fn ValueMissing(&self) -> bool { + return false; + } + fn TypeMismatch(&self) -> bool { + return false; + } + fn PatternMismatch(&self) -> bool { + return false; + } + fn TooLong(&self) -> bool { + return false; + } + fn TooShort(&self) -> bool { + return false; + } + fn RangeUnderflow(&self) -> bool { + return false; + } + fn RangeOverflow(&self) -> bool { + return false; + } + fn StepMismatch(&self) -> bool { + return false; + } + fn BadInput(&self) -> bool { + return false; + } + fn CustomError(&self) -> bool { + return false; + } +} diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs index cfce718bcbe4..b99beec5acc8 100644 --- a/components/script/dom/htmlselectelement.rs +++ b/components/script/dom/htmlselectelement.rs @@ -3,20 +3,24 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::attr::{Attr, AttrValue}; +use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; +use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods; use dom::bindings::codegen::Bindings::HTMLSelectElementBinding; use dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementMethods; +use dom::bindings::codegen::Bindings::ValidityStateBinding::ValidityStateMethods; use dom::bindings::codegen::UnionTypes::HTMLElementOrLong; use dom::bindings::codegen::UnionTypes::HTMLOptionElementOrHTMLOptGroupElement; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::document::Document; use dom::element::{AttributeMutation, Element}; +use dom::event::Event; use dom::htmlelement::HTMLElement; use dom::htmlfieldsetelement::HTMLFieldSetElement; use dom::htmlformelement::{FormDatumValue, FormControl, FormDatum, HTMLFormElement}; use dom::htmloptionelement::HTMLOptionElement; -use dom::node::{Node, UnbindContext, window_from_node}; +use dom::node::{Node, UnbindContext, window_from_node, document_from_node}; use dom::nodelist::NodeList; use dom::validation::Validatable; use dom::validitystate::ValidityState; @@ -25,6 +29,7 @@ use string_cache::Atom; use style::element_state::*; use util::str::DOMString; + #[dom_struct] pub struct HTMLSelectElement { htmlelement: HTMLElement @@ -232,8 +237,92 @@ impl VirtualMethods for HTMLSelectElement { _ => self.super_type().unwrap().parse_plain_attribute(local_name, value), } } + fn handle_event(&self, event: &Event) { + if let Some(s) = self.super_type() { + s.handle_event(event); + } + if event.type_() == atom!("click") && !event.DefaultPrevented() { + // TODO: Dispatch events for non activatable inputs + // https://html.spec.whatwg.org/multipage/#common-input-element-events + //TODO: set the editing position for text inputs + document_from_node(self).request_focus(self.upcast()); + } + } } -impl FormControl for HTMLSelectElement {} +impl FormControl for HTMLSelectElement { + fn candidate_for_validation(&self, element: &Element) -> bool { + if element.as_maybe_validatable().is_some() { + return true + } + else { + return false + } + } -impl Validatable for HTMLSelectElement {} + fn satisfies_constraints(&self, element: &Element) -> bool { + let vs = ValidityState::new(window_from_node(self).r(), element); + return vs.Valid() + } + fn ValueMissing(&self) -> bool { + let attr_value_check = self.upcast::().get_attribute_by_name(DOMString::from("required")) + .map(|s| s.Value()); + if attr_value_check.is_some() { + // let html_select_element = self.element.downcast::().unwrap(); + let input_value_check = self.get_value_for_validation(); + if input_value_check.is_some() { + return false; + } + else { + println!("Error - Value missing in html select area element"); + return true; + } + } + else { + return false; + } + } + fn TypeMismatch(&self) -> bool { + return false; + } + fn PatternMismatch(&self) -> bool { + return false; + } + fn TooLong(&self) -> bool { + return false; + } + fn TooShort(&self) -> bool { + return false; + } + fn RangeUnderflow(&self) -> bool { + return false; + } + fn RangeOverflow(&self) -> bool { + return false; + } + fn StepMismatch(&self) -> bool { + return false; + } + fn BadInput(&self) -> bool { + return false; + } + fn CustomError(&self) -> bool { + return false; + } + +} + +impl Validatable for HTMLSelectElement { + fn get_value_for_validation(&self) -> Option{ + /*let node = self.upcast::(); + for opt in node.traverse_preorder().filter_map(Root::downcast::) { + let element = opt.upcast::(); + if opt.Selected() && element.enabled_state() { + Some(self.Name()) + } + }*/ + + None + + } +} diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index 597c9a2bb60e..e045945752bd 100644 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -4,10 +4,12 @@ use dom::attr::{Attr, AttrValue}; use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding; use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; +use dom::bindings::codegen::Bindings::ValidityStateBinding::ValidityStateMethods; use dom::bindings::inheritance::Castable; use dom::bindings::js::{LayoutJS, Root}; use dom::bindings::reflector::{Reflectable}; @@ -22,8 +24,10 @@ use dom::htmlinputelement::ChangeEventRunnable; use dom::keyboardevent::KeyboardEvent; use dom::node::{ChildrenMutation, Node, NodeDamage, UnbindContext}; use dom::node::{document_from_node}; +use dom::node::{window_from_node}; use dom::nodelist::NodeList; use dom::validation::Validatable; +use dom::validitystate::ValidityState; use dom::virtualmethods::VirtualMethods; use msg::constellation_msg::ConstellationChan; use script_traits::ScriptMsg as ConstellationMsg; @@ -34,6 +38,8 @@ use style::element_state::*; use textinput::{KeyReaction, Lines, TextInput, SelectionDirection}; use util::str::DOMString; + + #[dom_struct] pub struct HTMLTextAreaElement { htmlelement: HTMLElement, @@ -390,6 +396,123 @@ impl VirtualMethods for HTMLTextAreaElement { } } -impl FormControl for HTMLTextAreaElement {} +impl FormControl for HTMLTextAreaElement { + fn candidate_for_validation(&self, element: &Element) -> bool { + if element.as_maybe_validatable().is_some() { + return true + } + else { + return false + } + } + + fn satisfies_constraints(&self, element: &Element) -> bool { + let vs = ValidityState::new(window_from_node(self).r(), element); + return vs.Valid() + } + fn ValueMissing(&self) -> bool { + let attr_value_check = self.upcast::().get_attribute_by_name(DOMString::from("required")) + .map(|s| s.Value()); + if attr_value_check.is_some() { + // let html_textarea_element = self.element.downcast::().unwrap(); + let input_value_check = self.get_value_for_validation(); + if input_value_check.is_some() { + return false; + } + else { + println!("Error - Value missing in html text area element"); + return true; + } + } + else { + return false; + } + } + fn TypeMismatch(&self) -> bool { + return false; + } + fn PatternMismatch(&self) -> bool { + return false; + } + fn TooLong(&self) -> bool { + let attr_value_check = self.upcast::().get_attribute_by_name(DOMString::from("maxlength")) + .map(|s| s.Value()); + match attr_value_check { + Some(attr_value) => { + let maxlength = attr_value.parse().unwrap(); + //let html_textarea_element = self.element.downcast::().unwrap(); + let input_value_check = self.get_value_for_validation(); + match input_value_check { + Some(input_value) => { + if input_value.len() > maxlength { + println!("Error - TooLong in text area"); + return true; + } + else { + return false; + } + }, + None => { + return false; + } + } + }, + None => { + return false; + } + } + } + fn TooShort(&self) -> bool { + let attr_value_check = self.upcast::().get_attribute_by_name(DOMString::from("minlength")) + .map(|s| s.Value()); + match attr_value_check { + Some(attr_value) => { + let minlength = attr_value.parse().unwrap(); + // let html_input_element = self.element.downcast::().unwrap(); + let input_value_check = self.get_value_for_validation(); + match input_value_check { + Some(input_value) => { + if input_value.len() < minlength { + println!("Error - TooShort html input element"); + return true; + } + else { + return false; + } + }, + None => { + return false; + } + } + }, + None => { + return false; + } + } + } + fn RangeUnderflow(&self) -> bool { + return false; + } + fn RangeOverflow(&self) -> bool { + return false; + } + fn StepMismatch(&self) -> bool { + return false; + } + fn BadInput(&self) -> bool { + return false; + } + fn CustomError(&self) -> bool { + return false; + } +} -impl Validatable for HTMLTextAreaElement {} +impl Validatable for HTMLTextAreaElement { + fn get_value_for_validation(&self) -> Option{ + if self.Value().is_empty() { + None + } else { + Some(self.Value()) + } + } +} diff --git a/components/script/dom/validation.rs b/components/script/dom/validation.rs index e9a6a1568482..687ca7675762 100644 --- a/components/script/dom/validation.rs +++ b/components/script/dom/validation.rs @@ -1,5 +1,8 @@ /* 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 http://mozilla.org/MPL/2.0/. */ +use util::str::DOMString; -pub trait Validatable {} +pub trait Validatable { + fn get_value_for_validation(&self) -> Option; +} diff --git a/components/script/dom/validitystate.rs b/components/script/dom/validitystate.rs index bd2cbb21aa7d..6a538865c38c 100644 --- a/components/script/dom/validitystate.rs +++ b/components/script/dom/validitystate.rs @@ -1,19 +1,25 @@ /* 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 http://mozilla.org/MPL/2.0/. */ - use dom::bindings::codegen::Bindings::ValidityStateBinding; use dom::bindings::codegen::Bindings::ValidityStateBinding::ValidityStateMethods; use dom::bindings::global::GlobalRef; +use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::element::Element; +use dom::htmlbuttonelement::HTMLButtonElement; +use dom::htmlformelement::FormControl; +use dom::htmlinputelement::HTMLInputElement; +use dom::htmlobjectelement::HTMLObjectElement; +use dom::htmlselectelement::HTMLSelectElement; +use dom::htmltextareaelement::HTMLTextAreaElement; +use dom::node::Node; use dom::window::Window; -// https://html.spec.whatwg.org/multipage/#validity-states -#[derive_JSTraceable] -#[derive_HeapSizeOf] -pub enum ValidityStatus { +/// https://html.spec.whatwg.org/multipage/#validity-states +#[derive(JSTraceable, HeapSizeOf)] +pub enum ValidityStates { ValueMissing, TypeMismatch, PatternMismatch, @@ -27,12 +33,12 @@ pub enum ValidityStatus { Valid } -// https://html.spec.whatwg.org/multipage/#validitystate +/// https://html.spec.whatwg.org/multipage/#validitystate #[dom_struct] pub struct ValidityState { reflector_: Reflector, element: JS, - state: ValidityStatus + state: ValidityStates } @@ -41,7 +47,7 @@ impl ValidityState { ValidityState { reflector_: Reflector::new(), element: JS::from_ref(element), - state: ValidityStatus::Valid + state: ValidityStates::Valid } } @@ -55,57 +61,134 @@ impl ValidityState { impl ValidityStateMethods for ValidityState { // https://html.spec.whatwg.org/multipage/#dom-validitystate-valuemissing - fn ValueMissing(&self) -> bool { - false - } + fn ValueMissing(&self) -> bool { return false; } + // https://html.spec.whatwg.org/multipage/#dom-validitystate-typemismatch - fn TypeMismatch(&self) -> bool { - false - } + fn TypeMismatch(&self) -> bool { return false; } + // https://html.spec.whatwg.org/multipage/#dom-validitystate-patternmismatch - fn PatternMismatch(&self) -> bool { - false - } + fn PatternMismatch(&self) -> bool { return false; } + // https://html.spec.whatwg.org/multipage/#dom-validitystate-toolong - fn TooLong(&self) -> bool { - false - } + fn TooLong(&self) -> bool { return false; } + // https://html.spec.whatwg.org/multipage/#dom-validitystate-tooshort - fn TooShort(&self) -> bool { - false - } + fn TooShort(&self) -> bool { return false; } + // https://html.spec.whatwg.org/multipage/#dom-validitystate-rangeunderflow - fn RangeUnderflow(&self) -> bool { - false - } + fn RangeUnderflow(&self) -> bool { return false; } + // https://html.spec.whatwg.org/multipage/#dom-validitystate-rangeoverflow - fn RangeOverflow(&self) -> bool { - false - } + fn RangeOverflow(&self) -> bool { return false; } + // https://html.spec.whatwg.org/multipage/#dom-validitystate-stepmismatch - fn StepMismatch(&self) -> bool { - false - } + fn StepMismatch(&self) -> bool { return false; } + // https://html.spec.whatwg.org/multipage/#dom-validitystate-badinput - fn BadInput(&self) -> bool { - false - } + fn BadInput(&self) -> bool { return false; } + // https://html.spec.whatwg.org/multipage/#dom-validitystate-customerror - fn CustomError(&self) -> bool { - false - } + fn CustomError(&self) -> bool { return false; } + // https://html.spec.whatwg.org/multipage/#dom-validitystate-valid fn Valid(&self) -> bool { - false + match self.element.upcast::().type_id() { + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) => { + let html_input_element = self.element.downcast::().unwrap(); + return !( + html_input_element.ValueMissing()| + html_input_element.TypeMismatch()| + html_input_element.PatternMismatch()| + html_input_element.TooLong()| + html_input_element.TooShort()| + html_input_element.RangeUnderflow()| + html_input_element.RangeOverflow()| + html_input_element.StepMismatch()| + html_input_element.BadInput()| + html_input_element.CustomError()); + + }, + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) => { + let html_button_element = self.element.downcast::().unwrap(); + return !( + html_button_element.ValueMissing()| + html_button_element.TypeMismatch()| + html_button_element.PatternMismatch()| + html_button_element.TooLong()| + html_button_element.TooShort()| + html_button_element.RangeUnderflow()| + html_button_element.RangeOverflow()| + html_button_element.StepMismatch()| + html_button_element.BadInput()| + html_button_element.CustomError()); + }, + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement)) => { + let html_object_element = self.element.downcast::().unwrap(); + return !( + html_object_element.ValueMissing()| + html_object_element.TypeMismatch()| + html_object_element.PatternMismatch()| + html_object_element.TooLong()| + html_object_element.TooShort()| + html_object_element.RangeUnderflow()| + html_object_element.RangeOverflow()| + html_object_element.StepMismatch()| + html_object_element.BadInput()| + html_object_element.CustomError()); + }, + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) => { + let html_select_element = self.element.downcast::().unwrap(); + return !( + html_select_element.ValueMissing()| + html_select_element.TypeMismatch()| + html_select_element.PatternMismatch()| + html_select_element.TooLong()| + html_select_element.TooShort()| + html_select_element.RangeUnderflow()| + html_select_element.RangeOverflow()| + html_select_element.StepMismatch()| + html_select_element.BadInput()| + html_select_element.CustomError()); + }, + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => { + let html_textarea_element = self.element.downcast::().unwrap(); + return !( + html_textarea_element.ValueMissing()| + html_textarea_element.TypeMismatch()| + html_textarea_element.PatternMismatch()| + html_textarea_element.TooLong()| + html_textarea_element.TooShort()| + html_textarea_element.RangeUnderflow()| + html_textarea_element.RangeOverflow()| + html_textarea_element.StepMismatch()| + html_textarea_element.BadInput()| + html_textarea_element.CustomError()); + }, + NodeTypeId::Element(_) => { + return false; + } + NodeTypeId::CharacterData(_) => { + return false; + } + NodeTypeId::Document(_) => { + return false; + } + NodeTypeId::DocumentFragment => { + return false; + } + NodeTypeId::DocumentType => { + return false; + } + }; } } diff --git a/components/script/dom/webidls/HTMLFormElement.webidl b/components/script/dom/webidls/HTMLFormElement.webidl index a56b83235b66..f96596b34d95 100644 --- a/components/script/dom/webidls/HTMLFormElement.webidl +++ b/components/script/dom/webidls/HTMLFormElement.webidl @@ -22,8 +22,10 @@ interface HTMLFormElement : HTMLElement { void submit(); void reset(); - //boolean checkValidity(); - //boolean reportValidity(); + // https://html.spec.whatwg.org/multipage/#the-form-element:statically-validate-the-constraints + boolean check_validity(); + // https://html.spec.whatwg.org/multipage/#the-form-element:interactively-validate-the-constraints + boolean report_validity(); //void requestAutocomplete(); };