1 /* 2 * This file is part of the WebKit project. 3 * 4 * Copyright (C) 2009 Michelangelo De Simone <micdesim (at) gmail.com> 5 * Copyright (C) 2010 Apple Inc. All rights reserved. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Library General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Library General Public License for more details. 16 * 17 * You should have received a copy of the GNU Library General Public License 18 * along with this library; see the file COPYING.LIB. If not, write to 19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 * Boston, MA 02110-1301, USA. 21 * 22 */ 23 24 #include "config.h" 25 #include "ValidityState.h" 26 27 #include "HTMLInputElement.h" 28 #include "HTMLNames.h" 29 #include "KURL.h" 30 #include "LocalizedStrings.h" 31 #include "RegularExpression.h" 32 #include <wtf/StdLibExtras.h> 33 34 namespace WebCore { 35 36 using namespace HTMLNames; 37 38 static const char emailPattern[] = 39 "[a-z0-9!#$%&'*+/=?^_`{|}~.-]+" // local part 40 "@" 41 "[a-z0-9-]+(\\.[a-z0-9-]+)+"; // domain part 42 43 String ValidityState::validationMessage() const 44 { 45 if (!m_control->willValidate()) 46 return String(); 47 48 if (customError()) 49 return m_customErrorMessage; 50 if (valueMissing()) 51 return validationMessageValueMissingText(); 52 if (typeMismatch()) 53 return validationMessageTypeMismatchText(); 54 if (patternMismatch()) 55 return validationMessagePatternMismatchText(); 56 if (tooLong()) 57 return validationMessageTooLongText(); 58 if (rangeUnderflow()) 59 return validationMessageRangeUnderflowText(); 60 if (rangeOverflow()) 61 return validationMessageRangeOverflowText(); 62 if (stepMismatch()) 63 return validationMessageStepMismatchText(); 64 65 return String(); 66 } 67 68 bool ValidityState::typeMismatch() const 69 { 70 if (!m_control->hasTagName(inputTag)) 71 return false; 72 73 HTMLInputElement* input = static_cast<HTMLInputElement*>(m_control); 74 String value = input->value(); 75 76 if (value.isEmpty()) 77 return false; 78 79 switch (input->inputType()) { 80 case HTMLInputElement::COLOR: 81 return !isValidColorString(value); 82 case HTMLInputElement::NUMBER: 83 return !HTMLInputElement::formStringToDouble(value, 0); 84 case HTMLInputElement::URL: 85 return !KURL(KURL(), value).isValid(); 86 case HTMLInputElement::EMAIL: { 87 if (!input->multiple()) 88 return !isValidEmailAddress(value); 89 Vector<String> addresses; 90 value.split(',', addresses); 91 for (unsigned i = 0; i < addresses.size(); ++i) { 92 if (!isValidEmailAddress(addresses[i])) 93 return true; 94 } 95 return false; 96 } 97 case HTMLInputElement::DATE: 98 case HTMLInputElement::DATETIME: 99 case HTMLInputElement::DATETIMELOCAL: 100 case HTMLInputElement::MONTH: 101 case HTMLInputElement::TIME: 102 case HTMLInputElement::WEEK: 103 return !HTMLInputElement::formStringToDateComponents(input->inputType(), value, 0); 104 case HTMLInputElement::BUTTON: 105 case HTMLInputElement::CHECKBOX: 106 case HTMLInputElement::FILE: 107 case HTMLInputElement::HIDDEN: 108 case HTMLInputElement::IMAGE: 109 case HTMLInputElement::ISINDEX: 110 case HTMLInputElement::PASSWORD: 111 case HTMLInputElement::RADIO: 112 case HTMLInputElement::RANGE: 113 case HTMLInputElement::RESET: 114 case HTMLInputElement::SEARCH: 115 case HTMLInputElement::SUBMIT: 116 case HTMLInputElement::TELEPHONE: // FIXME: Is there validation for <input type=telephone>? 117 case HTMLInputElement::TEXT: 118 return false; 119 } 120 121 ASSERT_NOT_REACHED(); 122 return false; 123 } 124 125 bool ValidityState::rangeUnderflow() const 126 { 127 if (!m_control->hasTagName(inputTag)) 128 return false; 129 return static_cast<HTMLInputElement*>(m_control)->rangeUnderflow(); 130 } 131 132 bool ValidityState::rangeOverflow() const 133 { 134 if (!m_control->hasTagName(inputTag)) 135 return false; 136 return static_cast<HTMLInputElement*>(m_control)->rangeOverflow(); 137 } 138 139 bool ValidityState::stepMismatch() const 140 { 141 if (!m_control->hasTagName(inputTag)) 142 return false; 143 return static_cast<HTMLInputElement*>(m_control)->stepMismatch(); 144 } 145 146 bool ValidityState::valid() const 147 { 148 bool someError = typeMismatch() || stepMismatch() || rangeUnderflow() || rangeOverflow() 149 || tooLong() || patternMismatch() || valueMissing() || customError(); 150 return !someError; 151 } 152 153 bool ValidityState::isValidColorString(const String& value) 154 { 155 if (value.isEmpty()) 156 return false; 157 if (value[0] == '#') { 158 // We don't accept #rgb and #aarrggbb formats. 159 if (value.length() != 7) 160 return false; 161 } 162 Color color(value); // This accepts named colors such as "white". 163 return color.isValid() && !color.hasAlpha(); 164 } 165 166 bool ValidityState::isValidEmailAddress(const String& address) 167 { 168 int addressLength = address.length(); 169 if (!addressLength) 170 return false; 171 172 DEFINE_STATIC_LOCAL(const RegularExpression, regExp, (emailPattern, TextCaseInsensitive)); 173 174 int matchLength; 175 int matchOffset = regExp.match(address, 0, &matchLength); 176 177 return matchOffset == 0 && matchLength == addressLength; 178 } 179 180 } // namespace 181