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 Google 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 "core/html/EmailInputType.h"
     26 
     27 #include "core/html/HTMLInputElement.h"
     28 #include "core/html/InputTypeNames.h"
     29 #include "core/html/parser/HTMLParserIdioms.h"
     30 #include "core/page/Chrome.h"
     31 #include "core/page/ChromeClient.h"
     32 #include "core/platform/LocalizedStrings.h"
     33 #include "core/platform/text/RegularExpression.h"
     34 #include "public/platform/Platform.h"
     35 #include "wtf/PassOwnPtr.h"
     36 #include "wtf/text/StringBuilder.h"
     37 #include <unicode/uidna.h>
     38 
     39 namespace WebCore {
     40 
     41 static const char emailPattern[] =
     42     "[a-z0-9!#$%&'*+/=?^_`{|}~.-]+" // local part
     43     "@"
     44     "[a-z0-9-]+(\\.[a-z0-9-]+)*"; // domain part
     45 
     46 // RFC5321 says the maximum total length of a domain name is 255 octets.
     47 static const size_t maximumDomainNameLength = 255;
     48 static const int32_t idnaConversionOption = UIDNA_ALLOW_UNASSIGNED;
     49 
     50 static String convertEmailAddressToASCII(const String& address)
     51 {
     52     if (address.containsOnlyASCII())
     53         return address;
     54 
     55     size_t atPosition = address.find('@');
     56     if (atPosition == notFound)
     57         return address;
     58 
     59     UErrorCode error = U_ZERO_ERROR;
     60     UChar domainNameBuffer[maximumDomainNameLength];
     61     int32_t domainNameLength = uidna_IDNToASCII(address.charactersWithNullTermination().data() + atPosition + 1, address.length() - atPosition - 1, domainNameBuffer, WTF_ARRAY_LENGTH(domainNameBuffer), idnaConversionOption, 0, &error);
     62     if (error != U_ZERO_ERROR || domainNameLength <= 0)
     63         return address;
     64 
     65     StringBuilder builder;
     66     builder.append(address, 0, atPosition + 1);
     67     builder.append(domainNameBuffer, domainNameLength);
     68     return builder.toString();
     69 }
     70 
     71 String EmailInputType::convertEmailAddressToUnicode(const String& address) const
     72 {
     73     if (!address.containsOnlyASCII())
     74         return address;
     75 
     76     size_t atPosition = address.find('@');
     77     if (atPosition == notFound)
     78         return address;
     79 
     80     if (address.find("xn--", atPosition + 1) == notFound)
     81         return address;
     82 
     83     ChromeClient* chromeClient = chrome() ? chrome()->client() : 0;
     84     if (!chromeClient)
     85         return address;
     86 
     87     String languages = chromeClient->acceptLanguages();
     88     String unicodeHost = WebKit::Platform::current()->convertIDNToUnicode(address.substring(atPosition + 1), languages);
     89     StringBuilder builder;
     90     builder.append(address, 0, atPosition + 1);
     91     builder.append(unicodeHost);
     92     return builder.toString();
     93 }
     94 
     95 static bool isValidEmailAddress(const String& address)
     96 {
     97     int addressLength = address.length();
     98     if (!addressLength)
     99         return false;
    100 
    101     DEFINE_STATIC_LOCAL(const RegularExpression, regExp, (emailPattern, TextCaseInsensitive));
    102 
    103     int matchLength;
    104     int matchOffset = regExp.match(address, 0, &matchLength);
    105 
    106     return !matchOffset && matchLength == addressLength;
    107 }
    108 
    109 PassOwnPtr<InputType> EmailInputType::create(HTMLInputElement* element)
    110 {
    111     return adoptPtr(new EmailInputType(element));
    112 }
    113 
    114 void EmailInputType::attach()
    115 {
    116     TextFieldInputType::attach();
    117     observeFeatureIfVisible(UseCounter::InputTypeEmail);
    118 }
    119 
    120 const AtomicString& EmailInputType::formControlType() const
    121 {
    122     return InputTypeNames::email();
    123 }
    124 
    125 bool EmailInputType::typeMismatchFor(const String& value) const
    126 {
    127     if (value.isEmpty())
    128         return false;
    129     if (!element()->multiple())
    130         return !isValidEmailAddress(value);
    131     Vector<String> addresses;
    132     value.split(',', true, addresses);
    133     for (unsigned i = 0; i < addresses.size(); ++i) {
    134         if (!isValidEmailAddress(stripLeadingAndTrailingHTMLSpaces(addresses[i])))
    135             return true;
    136     }
    137     return false;
    138 }
    139 
    140 bool EmailInputType::typeMismatch() const
    141 {
    142     return typeMismatchFor(element()->value());
    143 }
    144 
    145 String EmailInputType::typeMismatchText() const
    146 {
    147     return element()->multiple() ? validationMessageTypeMismatchForMultipleEmailText() : validationMessageTypeMismatchForEmailText();
    148 }
    149 
    150 bool EmailInputType::isEmailField() const
    151 {
    152     return true;
    153 }
    154 
    155 bool EmailInputType::supportsSelectionAPI() const
    156 {
    157     return false;
    158 }
    159 
    160 String EmailInputType::sanitizeValue(const String& proposedValue) const
    161 {
    162     String noLineBreakValue = proposedValue.removeCharacters(isHTMLLineBreak);
    163     if (!element()->multiple())
    164         return stripLeadingAndTrailingHTMLSpaces(noLineBreakValue);
    165     Vector<String> addresses;
    166     noLineBreakValue.split(',', true, addresses);
    167     StringBuilder strippedValue;
    168     for (size_t i = 0; i < addresses.size(); ++i) {
    169         if (i > 0)
    170             strippedValue.append(",");
    171         strippedValue.append(stripLeadingAndTrailingHTMLSpaces(addresses[i]));
    172     }
    173     return strippedValue.toString();
    174 }
    175 
    176 String EmailInputType::convertFromVisibleValue(const String& visibleValue) const
    177 {
    178     String sanitizedValue = sanitizeValue(visibleValue);
    179     if (!element()->multiple())
    180         return convertEmailAddressToASCII(sanitizedValue);
    181     Vector<String> addresses;
    182     sanitizedValue.split(',', true, addresses);
    183     StringBuilder builder;
    184     builder.reserveCapacity(sanitizedValue.length());
    185     for (size_t i = 0; i < addresses.size(); ++i) {
    186         if (i > 0)
    187             builder.append(",");
    188         builder.append(convertEmailAddressToASCII(addresses[i]));
    189     }
    190     return builder.toString();
    191 }
    192 
    193 String EmailInputType::visibleValue() const
    194 {
    195     String value = element()->value();
    196     if (!element()->multiple())
    197         return convertEmailAddressToUnicode(value);
    198 
    199     Vector<String> addresses;
    200     value.split(',', true, addresses);
    201     StringBuilder builder;
    202     builder.reserveCapacity(value.length());
    203     for (size_t i = 0; i < addresses.size(); ++i) {
    204         if (i > 0)
    205             builder.append(",");
    206         builder.append(convertEmailAddressToUnicode(addresses[i]));
    207     }
    208     return builder.toString();
    209 }
    210 
    211 } // namespace WebCore
    212