Bug 1345767 - Part 4: Factor HasTypeMismatch() out of HTMLInputElement. r=smaug

MozReview-Commit-ID: 7kW0Ojnp2OE

--HG--
extra : rebase_source : a4107eecb5e41c886b71adf55f91f0e2b19d047b
This commit is contained in:
Jessica Jong 2017-05-04 15:08:24 +08:00
Родитель ee7fa8727c
Коммит ea4dea7a00
6 изменённых файлов: 234 добавлений и 141 удалений

Просмотреть файл

@ -51,7 +51,6 @@
#include "nsIServiceManager.h"
#include "nsError.h"
#include "nsIEditor.h"
#include "nsIIOService.h"
#include "nsDocument.h"
#include "nsAttrValueOrString.h"
#include "nsDateTimeControlFrame.h"
@ -86,8 +85,6 @@
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileList.h"
#include "nsIFile.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIContentPrefService.h"
#include "nsIMIMEService.h"
@ -7550,40 +7547,7 @@ HTMLInputElement::IsValueMissing() const
bool
HTMLInputElement::HasTypeMismatch() const
{
if (mType != NS_FORM_INPUT_EMAIL && mType != NS_FORM_INPUT_URL) {
return false;
}
nsAutoString value;
GetNonFileValueInternal(value);
if (value.IsEmpty()) {
return false;
}
if (mType == NS_FORM_INPUT_EMAIL) {
return HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)
? !IsValidEmailAddressList(value) : !IsValidEmailAddress(value);
} else if (mType == NS_FORM_INPUT_URL) {
/**
* TODO:
* The URL is not checked as the HTML5 specifications want it to be because
* there is no code to check for a valid URI/IRI according to 3986 and 3987
* RFC's at the moment, see bug 561586.
*
* RFC 3987 (IRI) implementation: bug 42899
*
* HTML5 specifications:
* http://dev.w3.org/html5/spec/infrastructure.html#valid-url
*/
nsCOMPtr<nsIIOService> ioService = do_GetIOService();
nsCOMPtr<nsIURI> uri;
return !NS_SUCCEEDED(ioService->NewURI(NS_ConvertUTF16toUTF8(value), nullptr,
nullptr, getter_AddRefs(uri)));
}
return false;
return mInputType->HasTypeMismatch();
}
bool
@ -8159,88 +8123,6 @@ HTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
return rv;
}
//static
bool
HTMLInputElement::IsValidEmailAddressList(const nsAString& aValue)
{
HTMLSplitOnSpacesTokenizer tokenizer(aValue, ',');
while (tokenizer.hasMoreTokens()) {
if (!IsValidEmailAddress(tokenizer.nextToken())) {
return false;
}
}
return !tokenizer.separatorAfterCurrentToken();
}
//static
bool
HTMLInputElement::IsValidEmailAddress(const nsAString& aValue)
{
// Email addresses can't be empty and can't end with a '.' or '-'.
if (aValue.IsEmpty() || aValue.Last() == '.' || aValue.Last() == '-') {
return false;
}
uint32_t atPos;
nsAutoCString value;
if (!PunycodeEncodeEmailAddress(aValue, value, &atPos) ||
atPos == (uint32_t)kNotFound || atPos == 0 || atPos == value.Length() - 1) {
// Could not encode, or "@" was not found, or it was at the start or end
// of the input - in all cases, not a valid email address.
return false;
}
uint32_t length = value.Length();
uint32_t i = 0;
// Parsing the username.
for (; i < atPos; ++i) {
char16_t c = value[i];
// The username characters have to be in this list to be valid.
if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) ||
c == '.' || c == '!' || c == '#' || c == '$' || c == '%' ||
c == '&' || c == '\''|| c == '*' || c == '+' || c == '-' ||
c == '/' || c == '=' || c == '?' || c == '^' || c == '_' ||
c == '`' || c == '{' || c == '|' || c == '}' || c == '~' )) {
return false;
}
}
// Skip the '@'.
++i;
// The domain name can't begin with a dot or a dash.
if (value[i] == '.' || value[i] == '-') {
return false;
}
// Parsing the domain name.
for (; i < length; ++i) {
char16_t c = value[i];
if (c == '.') {
// A dot can't follow a dot or a dash.
if (value[i-1] == '.' || value[i-1] == '-') {
return false;
}
} else if (c == '-'){
// A dash can't follow a dot.
if (value[i-1] == '.') {
return false;
}
} else if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) ||
c == '-')) {
// The domain characters have to be in this list to be valid.
return false;
}
}
return true;
}
NS_IMETHODIMP_(bool)
HTMLInputElement::IsSingleLineTextControl() const
{

Просмотреть файл

@ -924,28 +924,6 @@ protected:
VALUE_MODE_FILENAME
};
/**
* This helper method returns true if aValue is a valid email address.
* This is following the HTML5 specification:
* http://dev.w3.org/html5/spec/forms.html#valid-e-mail-address
*
* @param aValue the email address to check.
* @result whether the given string is a valid email address.
*/
static bool IsValidEmailAddress(const nsAString& aValue);
/**
* This helper method returns true if aValue is a valid email address list.
* Email address list is a list of email address separated by comas (,) which
* can be surrounded by space charecters.
* This is following the HTML5 specification:
* http://dev.w3.org/html5/spec/forms.html#valid-e-mail-address-list
*
* @param aValue the email address list to check.
* @result whether the given string is a valid email address list.
*/
static bool IsValidEmailAddressList(const nsAString& aValue);
/**
* This helper method convert a sub-string that contains only digits to a
* number (unsigned int given that it can't contain a minus sign).

Просмотреть файл

@ -114,6 +114,12 @@ InputType::IsValueEmpty() const
return mInputElement->IsValueEmpty();
}
void
InputType::GetNonFileValueInternal(nsAString& aValue) const
{
return mInputElement->GetNonFileValueInternal(aValue);
}
void
InputType::DropReference()
{
@ -138,3 +144,9 @@ InputType::IsValueMissing() const
{
return false;
}
bool
InputType::HasTypeMismatch() const
{
return false;
}

Просмотреть файл

@ -9,6 +9,7 @@
#include <stdint.h>
#include "mozilla/UniquePtr.h"
#include "nsString.h"
namespace mozilla {
namespace dom {
@ -38,6 +39,7 @@ public:
virtual bool IsTooLong() const;
virtual bool IsTooShort() const;
virtual bool IsValueMissing() const;
virtual bool HasTypeMismatch() const;
protected:
explicit InputType(mozilla::dom::HTMLInputElement* aInputElement)
@ -62,6 +64,10 @@ protected:
*/
bool IsValueEmpty() const;
// A getter for callers that know we're not dealing with a file input, so they
// don't have to think about the caller type.
void GetNonFileValueInternal(nsAString& aValue) const;
mozilla::dom::HTMLInputElement* mInputElement;
};

Просмотреть файл

@ -8,6 +8,12 @@
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "HTMLSplitOnSpacesTokenizer.h"
#include "nsCRTGlue.h"
#include "nsIIDNService.h"
#include "nsIIOService.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
bool
SingleLineTextInputTypeBase::IsMutable() const
@ -61,3 +67,166 @@ SingleLineTextInputTypeBase::IsValueMissing() const
return IsValueEmpty();
}
/* input type=url */
bool
URLInputType::HasTypeMismatch() const
{
nsAutoString value;
GetNonFileValueInternal(value);
if (value.IsEmpty()) {
return false;
}
/**
* TODO:
* The URL is not checked as the HTML5 specifications want it to be because
* there is no code to check for a valid URI/IRI according to 3986 and 3987
* RFC's at the moment, see bug 561586.
*
* RFC 3987 (IRI) implementation: bug 42899
*
* HTML5 specifications:
* http://dev.w3.org/html5/spec/infrastructure.html#valid-url
*/
nsCOMPtr<nsIIOService> ioService = do_GetIOService();
nsCOMPtr<nsIURI> uri;
return !NS_SUCCEEDED(ioService->NewURI(NS_ConvertUTF16toUTF8(value), nullptr,
nullptr, getter_AddRefs(uri)));
}
/* input type=email */
bool
EmailInputType::HasTypeMismatch() const
{
nsAutoString value;
GetNonFileValueInternal(value);
if (value.IsEmpty()) {
return false;
}
return mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ?
!IsValidEmailAddressList(value) : !IsValidEmailAddress(value);
}
/* static */ bool
EmailInputType::IsValidEmailAddressList(const nsAString& aValue)
{
HTMLSplitOnSpacesTokenizer tokenizer(aValue, ',');
while (tokenizer.hasMoreTokens()) {
if (!IsValidEmailAddress(tokenizer.nextToken())) {
return false;
}
}
return !tokenizer.separatorAfterCurrentToken();
}
/* static */ bool
EmailInputType::IsValidEmailAddress(const nsAString& aValue)
{
// Email addresses can't be empty and can't end with a '.' or '-'.
if (aValue.IsEmpty() || aValue.Last() == '.' || aValue.Last() == '-') {
return false;
}
uint32_t atPos;
nsAutoCString value;
if (!PunycodeEncodeEmailAddress(aValue, value, &atPos) ||
atPos == (uint32_t)kNotFound || atPos == 0 || atPos == value.Length() - 1) {
// Could not encode, or "@" was not found, or it was at the start or end
// of the input - in all cases, not a valid email address.
return false;
}
uint32_t length = value.Length();
uint32_t i = 0;
// Parsing the username.
for (; i < atPos; ++i) {
char16_t c = value[i];
// The username characters have to be in this list to be valid.
if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) ||
c == '.' || c == '!' || c == '#' || c == '$' || c == '%' ||
c == '&' || c == '\''|| c == '*' || c == '+' || c == '-' ||
c == '/' || c == '=' || c == '?' || c == '^' || c == '_' ||
c == '`' || c == '{' || c == '|' || c == '}' || c == '~' )) {
return false;
}
}
// Skip the '@'.
++i;
// The domain name can't begin with a dot or a dash.
if (value[i] == '.' || value[i] == '-') {
return false;
}
// Parsing the domain name.
for (; i < length; ++i) {
char16_t c = value[i];
if (c == '.') {
// A dot can't follow a dot or a dash.
if (value[i-1] == '.' || value[i-1] == '-') {
return false;
}
} else if (c == '-'){
// A dash can't follow a dot.
if (value[i-1] == '.') {
return false;
}
} else if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) ||
c == '-')) {
// The domain characters have to be in this list to be valid.
return false;
}
}
return true;
}
/* static */ bool
EmailInputType::PunycodeEncodeEmailAddress(const nsAString& aEmail,
nsAutoCString& aEncodedEmail,
uint32_t* aIndexOfAt)
{
nsAutoCString value = NS_ConvertUTF16toUTF8(aEmail);
*aIndexOfAt = (uint32_t)value.FindChar('@');
if (*aIndexOfAt == (uint32_t)kNotFound ||
*aIndexOfAt == value.Length() - 1) {
aEncodedEmail = value;
return true;
}
nsCOMPtr<nsIIDNService> idnSrv = do_GetService(NS_IDNSERVICE_CONTRACTID);
if (!idnSrv) {
NS_ERROR("nsIIDNService isn't present!");
return false;
}
uint32_t indexOfDomain = *aIndexOfAt + 1;
const nsDependentCSubstring domain = Substring(value, indexOfDomain);
bool ace;
if (NS_SUCCEEDED(idnSrv->IsACE(domain, &ace)) && !ace) {
nsAutoCString domainACE;
if (NS_FAILED(idnSrv->ConvertUTF8toACE(domain, domainACE))) {
return false;
}
value.Replace(indexOfDomain, domain.Length(), domainACE);
}
aEncodedEmail = value;
return true;
}

Просмотреть файл

@ -85,6 +85,8 @@ public:
return new (aMemory) URLInputType(aInputElement);
}
bool HasTypeMismatch() const override;
private:
explicit URLInputType(mozilla::dom::HTMLInputElement* aInputElement)
: SingleLineTextInputTypeBase(aInputElement)
@ -101,10 +103,54 @@ public:
return new (aMemory) EmailInputType(aInputElement);
}
bool HasTypeMismatch() const override;
private:
explicit EmailInputType(mozilla::dom::HTMLInputElement* aInputElement)
: SingleLineTextInputTypeBase(aInputElement)
{}
/**
* This helper method returns true if aValue is a valid email address.
* This is following the HTML5 specification:
* http://dev.w3.org/html5/spec/forms.html#valid-e-mail-address
*
* @param aValue the email address to check.
* @result whether the given string is a valid email address.
*/
static bool IsValidEmailAddress(const nsAString& aValue);
/**
* This helper method returns true if aValue is a valid email address list.
* Email address list is a list of email address separated by comas (,) which
* can be surrounded by space charecters.
* This is following the HTML5 specification:
* http://dev.w3.org/html5/spec/forms.html#valid-e-mail-address-list
*
* @param aValue the email address list to check.
* @result whether the given string is a valid email address list.
*/
static bool IsValidEmailAddressList(const nsAString& aValue);
/**
* Takes aEmail and attempts to convert everything after the first "@"
* character (if anything) to punycode before returning the complete result
* via the aEncodedEmail out-param. The aIndexOfAt out-param is set to the
* index of the "@" character.
*
* If no "@" is found in aEmail, aEncodedEmail is simply set to aEmail and
* the aIndexOfAt out-param is set to kNotFound.
*
* Returns true in all cases unless an attempt to punycode encode fails. If
* false is returned, aEncodedEmail has not been set.
*
* This function exists because ConvertUTF8toACE() splits on ".", meaning that
* for 'user.name@sld.tld' it would treat "name@sld" as a label. We want to
* encode the domain part only.
*/
static bool PunycodeEncodeEmailAddress(const nsAString& aEmail,
nsAutoCString& aEncodedEmail,
uint32_t* aIndexOfAt);
};
// input type=password