This source file includes following definitions.
- convertEmailAddressToASCII
- convertEmailAddressToUnicode
- isInvalidLocalPartCharacter
- isInvalidDomainCharacter
- checkValidDotUsage
- isValidEmailAddress
- create
- countUsage
- formControlType
- findInvalidAddress
- typeMismatchFor
- typeMismatch
- typeMismatchText
- isEmailField
- supportsSelectionAPI
- sanitizeValue
- convertFromVisibleValue
- visibleValue
#include "config.h"
#include "core/html/forms/EmailInputType.h"
#include "InputTypeNames.h"
#include "bindings/v8/ScriptRegexp.h"
#include "core/html/HTMLInputElement.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "core/page/Chrome.h"
#include "core/page/ChromeClient.h"
#include "platform/text/PlatformLocale.h"
#include "public/platform/Platform.h"
#include "wtf/PassOwnPtr.h"
#include "wtf/text/StringBuilder.h"
#include <unicode/uidna.h>
namespace WebCore {
using blink::WebLocalizedString;
static const char localPartCharacters[] = "abcdefghijklmnopqrstuvwxyz0123456789!#$%&'*+/=?^_`{|}~.-";
static const char emailPattern[] =
"[a-z0-9!#$%&'*+/=?^_`{|}~.-]+"
"@"
"[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?"
"(?:\\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*";
static const size_t maximumDomainNameLength = 255;
static const int32_t idnaConversionOption = UIDNA_ALLOW_UNASSIGNED;
static String convertEmailAddressToASCII(const String& address)
{
if (address.containsOnlyASCII())
return address;
size_t atPosition = address.find('@');
if (atPosition == kNotFound)
return address;
UErrorCode error = U_ZERO_ERROR;
UChar domainNameBuffer[maximumDomainNameLength];
int32_t domainNameLength = uidna_IDNToASCII(address.charactersWithNullTermination().data() + atPosition + 1, address.length() - atPosition - 1, domainNameBuffer, WTF_ARRAY_LENGTH(domainNameBuffer), idnaConversionOption, 0, &error);
if (error != U_ZERO_ERROR || domainNameLength <= 0)
return address;
StringBuilder builder;
builder.append(address, 0, atPosition + 1);
builder.append(domainNameBuffer, domainNameLength);
return builder.toString();
}
String EmailInputType::convertEmailAddressToUnicode(const String& address) const
{
if (!address.containsOnlyASCII())
return address;
size_t atPosition = address.find('@');
if (atPosition == kNotFound)
return address;
if (address.find("xn--", atPosition + 1) == kNotFound)
return address;
if (!chrome())
return address;
String languages = chrome()->client().acceptLanguages();
String unicodeHost = blink::Platform::current()->convertIDNToUnicode(address.substring(atPosition + 1), languages);
StringBuilder builder;
builder.append(address, 0, atPosition + 1);
builder.append(unicodeHost);
return builder.toString();
}
static bool isInvalidLocalPartCharacter(UChar ch)
{
if (!isASCII(ch))
return true;
DEFINE_STATIC_LOCAL(const String, validCharacters, (localPartCharacters));
return validCharacters.find(toASCIILower(ch)) == kNotFound;
}
static bool isInvalidDomainCharacter(UChar ch)
{
if (!isASCII(ch))
return true;
return !isASCIILower(ch) && !isASCIIUpper(ch) && !isASCIIDigit(ch) && ch != '.' && ch != '-';
}
static bool checkValidDotUsage(const String& domain)
{
if (domain.isEmpty())
return true;
if (domain[0] == '.' || domain[domain.length() - 1] == '.')
return false;
return domain.find("..") == kNotFound;
}
static bool isValidEmailAddress(const String& address)
{
int addressLength = address.length();
if (!addressLength)
return false;
DEFINE_STATIC_LOCAL(const ScriptRegexp, regExp, (emailPattern, TextCaseInsensitive));
int matchLength;
int matchOffset = regExp.match(address, 0, &matchLength);
return !matchOffset && matchLength == addressLength;
}
PassRefPtr<InputType> EmailInputType::create(HTMLInputElement& element)
{
return adoptRef(new EmailInputType(element));
}
void EmailInputType::countUsage()
{
countUsageIfVisible(UseCounter::InputTypeEmail);
bool hasMaxLength = element().fastHasAttribute(HTMLNames::maxlengthAttr);
if (hasMaxLength)
countUsageIfVisible(UseCounter::InputTypeEmailMaxLength);
if (element().multiple()) {
countUsageIfVisible(UseCounter::InputTypeEmailMultiple);
if (hasMaxLength)
countUsageIfVisible(UseCounter::InputTypeEmailMultipleMaxLength);
}
}
const AtomicString& EmailInputType::formControlType() const
{
return InputTypeNames::email;
}
String EmailInputType::findInvalidAddress(const String& value) const
{
if (value.isEmpty())
return String();
if (!element().multiple())
return isValidEmailAddress(value) ? String() : value;
Vector<String> addresses;
value.split(',', true, addresses);
for (unsigned i = 0; i < addresses.size(); ++i) {
String stripped = stripLeadingAndTrailingHTMLSpaces(addresses[i]);
if (!isValidEmailAddress(stripped))
return stripped;
}
return String();
}
bool EmailInputType::typeMismatchFor(const String& value) const
{
return !findInvalidAddress(value).isNull();
}
bool EmailInputType::typeMismatch() const
{
return typeMismatchFor(element().value());
}
String EmailInputType::typeMismatchText() const
{
String invalidAddress = findInvalidAddress(element().value());
ASSERT(!invalidAddress.isNull());
if (invalidAddress.isEmpty())
return locale().queryString(WebLocalizedString::ValidationTypeMismatchForEmailEmpty);
String atSign = String("@");
size_t atIndex = invalidAddress.find('@');
if (atIndex == kNotFound)
return locale().queryString(WebLocalizedString::ValidationTypeMismatchForEmailNoAtSign, atSign, invalidAddress);
String unicodeAddress = convertEmailAddressToUnicode(invalidAddress);
String localPart = invalidAddress.left(atIndex);
String domain = invalidAddress.substring(atIndex + 1);
if (localPart.isEmpty())
return locale().queryString(WebLocalizedString::ValidationTypeMismatchForEmailEmptyLocal, atSign, unicodeAddress);
if (domain.isEmpty())
return locale().queryString(WebLocalizedString::ValidationTypeMismatchForEmailEmptyDomain, atSign, unicodeAddress);
size_t invalidCharIndex = localPart.find(isInvalidLocalPartCharacter);
if (invalidCharIndex != kNotFound) {
unsigned charLength = U_IS_LEAD(localPart[invalidCharIndex]) ? 2 : 1;
return locale().queryString(WebLocalizedString::ValidationTypeMismatchForEmailInvalidLocal, atSign, localPart.substring(invalidCharIndex, charLength));
}
invalidCharIndex = domain.find(isInvalidDomainCharacter);
if (invalidCharIndex != kNotFound) {
unsigned charLength = U_IS_LEAD(domain[invalidCharIndex]) ? 2 : 1;
return locale().queryString(WebLocalizedString::ValidationTypeMismatchForEmailInvalidDomain, atSign, domain.substring(invalidCharIndex, charLength));
}
if (!checkValidDotUsage(domain)) {
size_t atIndexInUnicode = unicodeAddress.find('@');
ASSERT(atIndexInUnicode != kNotFound);
return locale().queryString(WebLocalizedString::ValidationTypeMismatchForEmailInvalidDots, String("."), unicodeAddress.substring(atIndexInUnicode + 1));
}
if (element().multiple())
return locale().queryString(WebLocalizedString::ValidationTypeMismatchForMultipleEmail);
return locale().queryString(WebLocalizedString::ValidationTypeMismatchForEmail);
}
bool EmailInputType::isEmailField() const
{
return true;
}
bool EmailInputType::supportsSelectionAPI() const
{
return false;
}
String EmailInputType::sanitizeValue(const String& proposedValue) const
{
String noLineBreakValue = proposedValue.removeCharacters(isHTMLLineBreak);
if (!element().multiple())
return stripLeadingAndTrailingHTMLSpaces(noLineBreakValue);
Vector<String> addresses;
noLineBreakValue.split(',', true, addresses);
StringBuilder strippedValue;
for (size_t i = 0; i < addresses.size(); ++i) {
if (i > 0)
strippedValue.append(",");
strippedValue.append(stripLeadingAndTrailingHTMLSpaces(addresses[i]));
}
return strippedValue.toString();
}
String EmailInputType::convertFromVisibleValue(const String& visibleValue) const
{
String sanitizedValue = sanitizeValue(visibleValue);
if (!element().multiple())
return convertEmailAddressToASCII(sanitizedValue);
Vector<String> addresses;
sanitizedValue.split(',', true, addresses);
StringBuilder builder;
builder.reserveCapacity(sanitizedValue.length());
for (size_t i = 0; i < addresses.size(); ++i) {
if (i > 0)
builder.append(",");
builder.append(convertEmailAddressToASCII(addresses[i]));
}
return builder.toString();
}
String EmailInputType::visibleValue() const
{
String value = element().value();
if (!element().multiple())
return convertEmailAddressToUnicode(value);
Vector<String> addresses;
value.split(',', true, addresses);
StringBuilder builder;
builder.reserveCapacity(value.length());
for (size_t i = 0; i < addresses.size(); ++i) {
if (i > 0)
builder.append(",");
builder.append(convertEmailAddressToUnicode(addresses[i]));
}
return builder.toString();
}
}