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(); };