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