Bug 1345767 - Part 4: Factor HasTypeMismatch() out of HTMLInputElement. r?smaug
MozReview-Commit-ID: 7kW0Ojnp2OE
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -46,17 +46,16 @@
#include "nsIPresShell.h"
#include "nsIFormControlFrame.h"
#include "nsITextControlFrame.h"
#include "nsIFrame.h"
#include "nsRangeFrame.h"
#include "nsIServiceManager.h"
#include "nsError.h"
#include "nsIEditor.h"
-#include "nsIIOService.h"
#include "nsDocument.h"
#include "nsAttrValueOrString.h"
#include "nsDateTimeControlFrame.h"
#include "nsPresState.h"
#include "nsIDOMEvent.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMHTMLCollection.h"
@@ -81,18 +80,16 @@
#include "nsIRadioGroupContainer.h"
// input type=file
#include "mozilla/dom/FileSystemEntry.h"
#include "mozilla/dom/FileSystem.h"
#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"
#include "nsIObserverService.h"
#include "nsIPopupWindowManager.h"
#include "nsGlobalWindow.h"
// input type=image
@@ -7524,50 +7521,17 @@ HTMLInputElement::IsValueMissing() const
MOZ_ASSERT(mType != NS_FORM_INPUT_RADIO);
return mInputType->IsValueMissing();
}
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
HTMLInputElement::HasPatternMismatch() const
{
if (!DoesPatternApply() ||
!HasAttr(kNameSpaceID_None, nsGkAtoms::pattern)) {
return false;
@@ -8133,98 +8097,16 @@ HTMLInputElement::GetValidationMessage(n
}
default:
rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType);
}
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
{
return IsSingleLineTextControl(false);
}
NS_IMETHODIMP_(bool)
HTMLInputElement::IsTextArea() const
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -920,38 +920,16 @@ protected:
// On getting, returns "C:\fakepath\" followed by the file name of the
// first file of the selected files if any.
// On setting the empty string, empties the selected files list, otherwise
// throw the INVALID_STATE_ERR exception.
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).
* This method will return whether the sub-string is correctly formatted
* (ie. contains only digit) and it can be successfuly parsed to generate a
* number).
* If the method returns true, |aResult| will contained the parsed number.
*
* @param aValue the string on which the sub-string will be extracted and parsed.
--- a/dom/html/input/InputType.cpp
+++ b/dom/html/input/InputType.cpp
@@ -110,16 +110,22 @@ InputType::IsMutable() const
bool
InputType::IsValueEmpty() const
{
return mInputElement->IsValueEmpty();
}
void
+InputType::GetNonFileValueInternal(nsAString& aValue) const
+{
+ return mInputElement->GetNonFileValueInternal(aValue);
+}
+
+void
InputType::DropReference()
{
// Drop our (non ref-counted) reference.
mInputElement = nullptr;
}
bool
InputType::IsTooLong() const
@@ -133,8 +139,14 @@ InputType::IsTooShort() const
return false;
}
bool
InputType::IsValueMissing() const
{
return false;
}
+
+bool
+InputType::HasTypeMismatch() const
+{
+ return false;
+}
--- a/dom/html/input/InputType.h
+++ b/dom/html/input/InputType.h
@@ -4,16 +4,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef InputType_h__
#define InputType_h__
#include <stdint.h>
#include "mozilla/UniquePtr.h"
+#include "nsString.h"
namespace mozilla {
namespace dom {
class HTMLInputElement;
} // namespace dom
} // namespace mozilla
struct DoNotDelete;
@@ -33,16 +34,17 @@ public:
/**
* Drop the reference to the input element.
*/
void DropReference();
virtual bool IsTooLong() const;
virtual bool IsTooShort() const;
virtual bool IsValueMissing() const;
+ virtual bool HasTypeMismatch() const;
protected:
explicit InputType(mozilla::dom::HTMLInputElement* aInputElement)
: mInputElement(aInputElement)
{}
/**
* Get the mutable state of the element.
@@ -57,16 +59,20 @@ protected:
* Returns whether the input element's current value is the empty string.
* This only makes sense for some input types; does NOT make sense for file
* inputs.
*
* @return whether the input element's current value is the empty string.
*/
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;
};
// Custom deleter for UniquePtr<InputType> to avoid freeing memory pre-allocated
// for InputType, but we still need to call the destructor explictly.
struct DoNotDelete { void operator()(::InputType* p) { p->~InputType(); } };
#endif /* InputType_h__ */
--- a/dom/html/input/SingleLineTextInputTypes.cpp
+++ b/dom/html/input/SingleLineTextInputTypes.cpp
@@ -3,16 +3,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SingleLineTextInputTypes.h"
#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
{
return !mInputElement->IsDisabled() &&
!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly);
}
@@ -56,8 +62,171 @@ SingleLineTextInputTypeBase::IsValueMiss
}
if (!IsMutable()) {
return false;
}
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;
+}
--- a/dom/html/input/SingleLineTextInputTypes.h
+++ b/dom/html/input/SingleLineTextInputTypes.h
@@ -80,36 +80,82 @@ class URLInputType : public SingleLineTe
{
public:
static InputType*
Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory)
{
return new (aMemory) URLInputType(aInputElement);
}
+ bool HasTypeMismatch() const override;
+
private:
explicit URLInputType(mozilla::dom::HTMLInputElement* aInputElement)
: SingleLineTextInputTypeBase(aInputElement)
{}
};
// input type=email
class EmailInputType : public SingleLineTextInputTypeBase
{
public:
static InputType*
Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory)
{
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
class PasswordInputType : public SingleLineTextInputTypeBase
{
public:
static InputType*
Create(mozilla::dom::HTMLInputElement* aInputElement, void* aMemory)