From b8ce70065477216ba9780da863a0536a854cdb96 Mon Sep 17 00:00:00 2001 From: Gabriel Poesia Date: Mon, 10 Jul 2017 20:50:03 -0300 Subject: [PATCH] Finish implementation of HTML5 form validation. --- components/script/dom/htmlformelement.rs | 24 +++++++-- components/script/dom/htmlinputelement.rs | 61 ++++++++++++++++++++++- components/script/dom/validitystate.rs | 22 ++++---- 3 files changed, 91 insertions(+), 16 deletions(-) diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index 53867c755f82..dfd2dac46506 100755 --- 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::HTMLElementBinding::HTMLElementMethods; use dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding::HTMLFormControlsCollectionMethods; use dom::bindings::codegen::Bindings::HTMLFormElementBinding; use dom::bindings::codegen::Bindings::HTMLFormElementBinding::HTMLFormElementMethods; @@ -454,13 +455,18 @@ impl HTMLFormElement { /// 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() { + // Step 1-2 + 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 3 + // TODO: Replace println! by something more visible to the user when a better reporting method + // becomes available. + let ref first_invalid_element = unhandled_invalid_controls[0].as_html_element(); + println!("Validation error in element {}", + first_invalid_element.upcast::().get_string_attribute(&local_name!("name"))); + first_invalid_element.Focus(); // Step 4 Err(()) } @@ -757,6 +763,16 @@ impl FormSubmittableElement { unreachable!() } } + + fn as_html_element(&self) -> &HTMLElement { + match *self { + FormSubmittableElement::ButtonElement(ref button) => button.upcast(), + FormSubmittableElement::InputElement(ref input) => input.upcast(), + FormSubmittableElement::ObjectElement(ref object) => object.upcast(), + FormSubmittableElement::SelectElement(ref select) => select.upcast(), + FormSubmittableElement::TextAreaElement(ref textarea) => textarea.upcast() + } + } } #[derive(Copy, Clone, HeapSizeOf)] diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index fc20326b906e..92ac2121cb97 100755 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use caseless::compatibility_caseless_match_str; +use core::borrow::Borrow; use dom::activation::{Activatable, ActivationSource, synthetic_click_activation}; use dom::attr::Attr; use dom::bindings::cell::DOMRefCell; @@ -43,11 +44,13 @@ use mime_guess; use net_traits::{CoreResourceMsg, IpcSend}; use net_traits::blob_url_store::get_blob_origin; use net_traits::filemanager_thread::{FileManagerThreadMsg, FilterPattern}; +use regex::Regex; use script_layout_interface::rpc::TextIndexResponse; use script_traits::ScriptMsg as ConstellationMsg; use servo_atoms::Atom; use std::borrow::ToOwned; use std::cell::Cell; +use std::error::{Error as StdError}; use std::ops::Range; use style::attr::AttrValue; use style::element_state::*; @@ -1188,7 +1191,63 @@ impl Validatable for HTMLInputElement { true } fn validate(&self, _validate_flags: ValidationFlags) -> bool { - // call stub methods defined in validityState.rs file here according to the flags set in validate_flags + use dom::validitystate::*; + + let value = self.Value(); + let value_str: &str = value.borrow(); + // https://html.spec.whatwg.org/multipage/#suffering-from-being-missing + if _validate_flags.contains(VALUE_MISSING) { + if value_str.is_empty() { + return false; + } + } + // https://html.spec.whatwg.org/multipage/#suffering-from-a-type-mismatch + if _validate_flags.contains(TYPE_MISMATCH) { + let validation_patterns = match self.Type().borrow() { + "url" => vec!(".", "/"), + "email" => vec!("@"), + _ => vec!(), + }; + + if validation_patterns.iter().all(|&p| !value_str.contains(p)) { + return false; + } + } + // https://html.spec.whatwg.org/multipage/#suffering-from-a-pattern-mismatch + if _validate_flags.contains(PATTERN_MISMATCH) { + let pattern = self.Pattern(); + let pattern_str: &str = pattern.borrow(); + if pattern_str.len() > 0 { + let pattern_regex = format!("^{}$", pattern_str); + match Regex::new(pattern_regex.as_str()) { + Ok(regex) => { + if !regex.is_match(value_str) { + return false; + } + }, + Err(error) => { + // TODO When possible, report this error to the developer console, as suggested in + // https://html.spec.whatwg.org/multipage/#attr-input-pattern + println!("Invalid pattern for element {}: {}", + self.Name(), error.description()); + } + }; + } + } + // https://html.spec.whatwg.org/multipage/#suffering-from-being-too-long + if _validate_flags.contains(TOO_LONG) { + let maxlength = self.maxlength.get(); + if maxlength != DEFAULT_MAX_LENGTH && value_str.len() > (maxlength as usize) { + return false; + } + } + // https://html.spec.whatwg.org/multipage/#suffering-from-being-too-short + if _validate_flags.contains(TOO_SHORT) { + let minlength = self.minlength.get(); + if minlength != DEFAULT_MIN_LENGTH && value_str.len() < (minlength as usize) { + return false; + } + } true } } diff --git a/components/script/dom/validitystate.rs b/components/script/dom/validitystate.rs index 879438aa3893..8db74131aac9 100755 --- a/components/script/dom/validitystate.rs +++ b/components/script/dom/validitystate.rs @@ -70,56 +70,56 @@ impl ValidityState { impl ValidityStateMethods for ValidityState { // https://html.spec.whatwg.org/multipage/#dom-validitystate-valuemissing fn ValueMissing(&self) -> bool { - false + self.element.as_maybe_validatable().map_or(false, |e| e.validate(VALUE_MISSING)) } // https://html.spec.whatwg.org/multipage/#dom-validitystate-typemismatch fn TypeMismatch(&self) -> bool { - false + self.element.as_maybe_validatable().map_or(false, |e| e.validate(TYPE_MISMATCH)) } // https://html.spec.whatwg.org/multipage/#dom-validitystate-patternmismatch fn PatternMismatch(&self) -> bool { - false + self.element.as_maybe_validatable().map_or(false, |e| e.validate(PATTERN_MISMATCH)) } // https://html.spec.whatwg.org/multipage/#dom-validitystate-toolong fn TooLong(&self) -> bool { - false + self.element.as_maybe_validatable().map_or(false, |e| e.validate(TOO_LONG)) } // https://html.spec.whatwg.org/multipage/#dom-validitystate-tooshort fn TooShort(&self) -> bool { - false + self.element.as_maybe_validatable().map_or(false, |e| e.validate(TOO_SHORT)) } // https://html.spec.whatwg.org/multipage/#dom-validitystate-rangeunderflow fn RangeUnderflow(&self) -> bool { - false + self.element.as_maybe_validatable().map_or(false, |e| e.validate(RANGE_UNDERFLOW)) } // https://html.spec.whatwg.org/multipage/#dom-validitystate-rangeoverflow fn RangeOverflow(&self) -> bool { - false + self.element.as_maybe_validatable().map_or(false, |e| e.validate(RANGE_OVERFLOW)) } // https://html.spec.whatwg.org/multipage/#dom-validitystate-stepmismatch fn StepMismatch(&self) -> bool { - false + self.element.as_maybe_validatable().map_or(false, |e| e.validate(STEP_MISMATCH)) } // https://html.spec.whatwg.org/multipage/#dom-validitystate-badinput fn BadInput(&self) -> bool { - false + self.element.as_maybe_validatable().map_or(false, |e| e.validate(BAD_INPUT)) } // https://html.spec.whatwg.org/multipage/#dom-validitystate-customerror fn CustomError(&self) -> bool { - false + self.element.as_maybe_validatable().map_or(false, |e| e.validate(CUSTOM_ERROR)) } // https://html.spec.whatwg.org/multipage/#dom-validitystate-valid fn Valid(&self) -> bool { - false + !self.element.as_maybe_validatable().map_or(false, |e| e.validate(ValidationFlags::all())) } }