Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "SingleLineTextInputTypes.h"
8 :
9 : #include "mozilla/dom/HTMLInputElement.h"
10 : #include "mozilla/dom/BindingDeclarations.h"
11 : #include "HTMLSplitOnSpacesTokenizer.h"
12 : #include "nsContentUtils.h"
13 : #include "nsCRTGlue.h"
14 : #include "nsIIDNService.h"
15 : #include "nsIIOService.h"
16 : #include "nsNetCID.h"
17 : #include "nsNetUtil.h"
18 :
19 : bool
20 0 : SingleLineTextInputTypeBase::IsMutable() const
21 : {
22 0 : return !mInputElement->IsDisabled() &&
23 0 : !mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly);
24 : }
25 :
26 : bool
27 0 : SingleLineTextInputTypeBase::IsTooLong() const
28 : {
29 0 : int32_t maxLength = mInputElement->MaxLength();
30 :
31 : // Maxlength of -1 means attribute isn't set or parsing error.
32 0 : if (maxLength == -1) {
33 0 : return false;
34 : }
35 :
36 : int32_t textLength =
37 0 : mInputElement->InputTextLength(mozilla::dom::CallerType::System);
38 :
39 0 : return textLength > maxLength;
40 : }
41 :
42 : bool
43 0 : SingleLineTextInputTypeBase::IsTooShort() const
44 : {
45 0 : int32_t minLength = mInputElement->MinLength();
46 :
47 : // Minlength of -1 means attribute isn't set or parsing error.
48 0 : if (minLength == -1) {
49 0 : return false;
50 : }
51 :
52 : int32_t textLength =
53 0 : mInputElement->InputTextLength(mozilla::dom::CallerType::System);
54 :
55 0 : return textLength && textLength < minLength;
56 : }
57 :
58 : bool
59 30 : SingleLineTextInputTypeBase::IsValueMissing() const
60 : {
61 30 : if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
62 30 : return false;
63 : }
64 :
65 0 : if (!IsMutable()) {
66 0 : return false;
67 : }
68 :
69 0 : return IsValueEmpty();
70 : }
71 :
72 : bool
73 7 : SingleLineTextInputTypeBase::HasPatternMismatch() const
74 : {
75 14 : nsAutoString pattern;
76 7 : if (!mInputElement->GetAttr(kNameSpaceID_None, nsGkAtoms::pattern, pattern)) {
77 7 : return false;
78 : }
79 :
80 0 : nsAutoString value;
81 0 : GetNonFileValueInternal(value);
82 :
83 0 : if (value.IsEmpty()) {
84 0 : return false;
85 : }
86 :
87 0 : nsIDocument* doc = mInputElement->OwnerDoc();
88 :
89 0 : return !nsContentUtils::IsPatternMatching(value, pattern, doc);
90 : }
91 :
92 : /* input type=url */
93 :
94 : bool
95 0 : URLInputType::HasTypeMismatch() const
96 : {
97 0 : nsAutoString value;
98 0 : GetNonFileValueInternal(value);
99 :
100 0 : if (value.IsEmpty()) {
101 0 : return false;
102 : }
103 :
104 : /**
105 : * TODO:
106 : * The URL is not checked as the HTML5 specifications want it to be because
107 : * there is no code to check for a valid URI/IRI according to 3986 and 3987
108 : * RFC's at the moment, see bug 561586.
109 : *
110 : * RFC 3987 (IRI) implementation: bug 42899
111 : *
112 : * HTML5 specifications:
113 : * http://dev.w3.org/html5/spec/infrastructure.html#valid-url
114 : */
115 0 : nsCOMPtr<nsIIOService> ioService = do_GetIOService();
116 0 : nsCOMPtr<nsIURI> uri;
117 :
118 0 : return !NS_SUCCEEDED(ioService->NewURI(NS_ConvertUTF16toUTF8(value), nullptr,
119 : nullptr, getter_AddRefs(uri)));
120 :
121 : }
122 :
123 : nsresult
124 0 : URLInputType::GetTypeMismatchMessage(nsXPIDLString& aMessage)
125 : {
126 : return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
127 : "FormValidationInvalidURL",
128 0 : aMessage);
129 : }
130 :
131 : /* input type=email */
132 :
133 : bool
134 0 : EmailInputType::HasTypeMismatch() const
135 : {
136 0 : nsAutoString value;
137 0 : GetNonFileValueInternal(value);
138 :
139 0 : if (value.IsEmpty()) {
140 0 : return false;
141 : }
142 :
143 0 : return mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple) ?
144 0 : !IsValidEmailAddressList(value) : !IsValidEmailAddress(value);
145 : }
146 :
147 : bool
148 0 : EmailInputType::HasBadInput() const
149 : {
150 : // With regards to suffering from bad input the spec says that only the
151 : // punycode conversion works, so we don't care whether the email address is
152 : // valid or not here. (If the email address is invalid then we will be
153 : // suffering from a type mismatch.)
154 0 : nsAutoString value;
155 0 : nsAutoCString unused;
156 : uint32_t unused2;
157 0 : GetNonFileValueInternal(value);
158 0 : HTMLSplitOnSpacesTokenizer tokenizer(value, ',');
159 0 : while (tokenizer.hasMoreTokens()) {
160 0 : if (!PunycodeEncodeEmailAddress(tokenizer.nextToken(), unused, &unused2)) {
161 0 : return true;
162 : }
163 : }
164 0 : return false;
165 : }
166 :
167 : nsresult
168 0 : EmailInputType::GetTypeMismatchMessage(nsXPIDLString& aMessage)
169 : {
170 : return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
171 : "FormValidationInvalidEmail",
172 0 : aMessage);
173 : }
174 :
175 : nsresult
176 0 : EmailInputType::GetBadInputMessage(nsXPIDLString& aMessage)
177 : {
178 : return nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
179 : "FormValidationInvalidEmail",
180 0 : aMessage);
181 : }
182 :
183 : /* static */ bool
184 0 : EmailInputType::IsValidEmailAddressList(const nsAString& aValue)
185 : {
186 0 : HTMLSplitOnSpacesTokenizer tokenizer(aValue, ',');
187 :
188 0 : while (tokenizer.hasMoreTokens()) {
189 0 : if (!IsValidEmailAddress(tokenizer.nextToken())) {
190 0 : return false;
191 : }
192 : }
193 :
194 0 : return !tokenizer.separatorAfterCurrentToken();
195 : }
196 :
197 : /* static */ bool
198 0 : EmailInputType::IsValidEmailAddress(const nsAString& aValue)
199 : {
200 : // Email addresses can't be empty and can't end with a '.' or '-'.
201 0 : if (aValue.IsEmpty() || aValue.Last() == '.' || aValue.Last() == '-') {
202 0 : return false;
203 : }
204 :
205 : uint32_t atPos;
206 0 : nsAutoCString value;
207 0 : if (!PunycodeEncodeEmailAddress(aValue, value, &atPos) ||
208 0 : atPos == (uint32_t)kNotFound || atPos == 0 || atPos == value.Length() - 1) {
209 : // Could not encode, or "@" was not found, or it was at the start or end
210 : // of the input - in all cases, not a valid email address.
211 0 : return false;
212 : }
213 :
214 0 : uint32_t length = value.Length();
215 0 : uint32_t i = 0;
216 :
217 : // Parsing the username.
218 0 : for (; i < atPos; ++i) {
219 0 : char16_t c = value[i];
220 :
221 : // The username characters have to be in this list to be valid.
222 0 : if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) ||
223 0 : c == '.' || c == '!' || c == '#' || c == '$' || c == '%' ||
224 0 : c == '&' || c == '\''|| c == '*' || c == '+' || c == '-' ||
225 0 : c == '/' || c == '=' || c == '?' || c == '^' || c == '_' ||
226 0 : c == '`' || c == '{' || c == '|' || c == '}' || c == '~' )) {
227 0 : return false;
228 : }
229 : }
230 :
231 : // Skip the '@'.
232 0 : ++i;
233 :
234 : // The domain name can't begin with a dot or a dash.
235 0 : if (value[i] == '.' || value[i] == '-') {
236 0 : return false;
237 : }
238 :
239 : // Parsing the domain name.
240 0 : for (; i < length; ++i) {
241 0 : char16_t c = value[i];
242 :
243 0 : if (c == '.') {
244 : // A dot can't follow a dot or a dash.
245 0 : if (value[i-1] == '.' || value[i-1] == '-') {
246 0 : return false;
247 : }
248 0 : } else if (c == '-'){
249 : // A dash can't follow a dot.
250 0 : if (value[i-1] == '.') {
251 0 : return false;
252 : }
253 0 : } else if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) ||
254 : c == '-')) {
255 : // The domain characters have to be in this list to be valid.
256 0 : return false;
257 : }
258 : }
259 :
260 0 : return true;
261 : }
262 :
263 : /* static */ bool
264 0 : EmailInputType::PunycodeEncodeEmailAddress(const nsAString& aEmail,
265 : nsAutoCString& aEncodedEmail,
266 : uint32_t* aIndexOfAt)
267 : {
268 0 : nsAutoCString value = NS_ConvertUTF16toUTF8(aEmail);
269 0 : *aIndexOfAt = (uint32_t)value.FindChar('@');
270 :
271 0 : if (*aIndexOfAt == (uint32_t)kNotFound ||
272 0 : *aIndexOfAt == value.Length() - 1) {
273 0 : aEncodedEmail = value;
274 0 : return true;
275 : }
276 :
277 0 : nsCOMPtr<nsIIDNService> idnSrv = do_GetService(NS_IDNSERVICE_CONTRACTID);
278 0 : if (!idnSrv) {
279 0 : NS_ERROR("nsIIDNService isn't present!");
280 0 : return false;
281 : }
282 :
283 0 : uint32_t indexOfDomain = *aIndexOfAt + 1;
284 :
285 0 : const nsDependentCSubstring domain = Substring(value, indexOfDomain);
286 : bool ace;
287 0 : if (NS_SUCCEEDED(idnSrv->IsACE(domain, &ace)) && !ace) {
288 0 : nsAutoCString domainACE;
289 0 : if (NS_FAILED(idnSrv->ConvertUTF8toACE(domain, domainACE))) {
290 0 : return false;
291 : }
292 0 : value.Replace(indexOfDomain, domain.Length(), domainACE);
293 : }
294 :
295 0 : aEncodedEmail = value;
296 0 : return true;
297 9 : }
|