Home | History | Annotate | Download | only in html
      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