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 "mozilla/dom/InputType.h"
8
9 #include "mozilla/Assertions.h"
10 #include "mozilla/Likely.h"
11 #include "nsIFormControl.h"
12 #include "mozilla/dom/ButtonInputTypes.h"
13 #include "mozilla/dom/CheckableInputTypes.h"
14 #include "mozilla/dom/ColorInputType.h"
15 #include "mozilla/dom/DateTimeInputTypes.h"
16 #include "mozilla/dom/FileInputType.h"
17 #include "mozilla/dom/HiddenInputType.h"
18 #include "mozilla/dom/HTMLInputElement.h"
19 #include "mozilla/dom/NumericInputTypes.h"
20 #include "mozilla/dom/SingleLineTextInputTypes.h"
21
22 #include "nsContentUtils.h"
23
24 using namespace mozilla;
25 using namespace mozilla::dom;
26
27 const Decimal InputType::kStepAny = Decimal(0);
28
Create(HTMLInputElement * aInputElement,FormControlType aType,void * aMemory)29 /* static */ UniquePtr<InputType, InputType::DoNotDelete> InputType::Create(
30 HTMLInputElement* aInputElement, FormControlType aType, void* aMemory) {
31 UniquePtr<InputType, InputType::DoNotDelete> inputType;
32 switch (aType) {
33 // Single line text
34 case FormControlType::InputText:
35 inputType.reset(TextInputType::Create(aInputElement, aMemory));
36 break;
37 case FormControlType::InputTel:
38 inputType.reset(TelInputType::Create(aInputElement, aMemory));
39 break;
40 case FormControlType::InputEmail:
41 inputType.reset(EmailInputType::Create(aInputElement, aMemory));
42 break;
43 case FormControlType::InputSearch:
44 inputType.reset(SearchInputType::Create(aInputElement, aMemory));
45 break;
46 case FormControlType::InputPassword:
47 inputType.reset(PasswordInputType::Create(aInputElement, aMemory));
48 break;
49 case FormControlType::InputUrl:
50 inputType.reset(URLInputType::Create(aInputElement, aMemory));
51 break;
52 // Button
53 case FormControlType::InputButton:
54 inputType.reset(ButtonInputType::Create(aInputElement, aMemory));
55 break;
56 case FormControlType::InputSubmit:
57 inputType.reset(SubmitInputType::Create(aInputElement, aMemory));
58 break;
59 case FormControlType::InputImage:
60 inputType.reset(ImageInputType::Create(aInputElement, aMemory));
61 break;
62 case FormControlType::InputReset:
63 inputType.reset(ResetInputType::Create(aInputElement, aMemory));
64 break;
65 // Checkable
66 case FormControlType::InputCheckbox:
67 inputType.reset(CheckboxInputType::Create(aInputElement, aMemory));
68 break;
69 case FormControlType::InputRadio:
70 inputType.reset(RadioInputType::Create(aInputElement, aMemory));
71 break;
72 // Numeric
73 case FormControlType::InputNumber:
74 inputType.reset(NumberInputType::Create(aInputElement, aMemory));
75 break;
76 case FormControlType::InputRange:
77 inputType.reset(RangeInputType::Create(aInputElement, aMemory));
78 break;
79 // DateTime
80 case FormControlType::InputDate:
81 inputType.reset(DateInputType::Create(aInputElement, aMemory));
82 break;
83 case FormControlType::InputTime:
84 inputType.reset(TimeInputType::Create(aInputElement, aMemory));
85 break;
86 case FormControlType::InputMonth:
87 inputType.reset(MonthInputType::Create(aInputElement, aMemory));
88 break;
89 case FormControlType::InputWeek:
90 inputType.reset(WeekInputType::Create(aInputElement, aMemory));
91 break;
92 case FormControlType::InputDatetimeLocal:
93 inputType.reset(DateTimeLocalInputType::Create(aInputElement, aMemory));
94 break;
95 // Others
96 case FormControlType::InputColor:
97 inputType.reset(ColorInputType::Create(aInputElement, aMemory));
98 break;
99 case FormControlType::InputFile:
100 inputType.reset(FileInputType::Create(aInputElement, aMemory));
101 break;
102 case FormControlType::InputHidden:
103 inputType.reset(HiddenInputType::Create(aInputElement, aMemory));
104 break;
105 default:
106 inputType.reset(TextInputType::Create(aInputElement, aMemory));
107 }
108
109 return inputType;
110 }
111
IsMutable() const112 bool InputType::IsMutable() const { return !mInputElement->IsDisabled(); }
113
IsValueEmpty() const114 bool InputType::IsValueEmpty() const { return mInputElement->IsValueEmpty(); }
115
GetNonFileValueInternal(nsAString & aValue) const116 void InputType::GetNonFileValueInternal(nsAString& aValue) const {
117 return mInputElement->GetNonFileValueInternal(aValue);
118 }
119
SetValueInternal(const nsAString & aValue,const ValueSetterOptions & aOptions)120 nsresult InputType::SetValueInternal(const nsAString& aValue,
121 const ValueSetterOptions& aOptions) {
122 RefPtr<HTMLInputElement> inputElement(mInputElement);
123 return inputElement->SetValueInternal(aValue, aOptions);
124 }
125
GetStepBase() const126 Decimal InputType::GetStepBase() const { return mInputElement->GetStepBase(); }
127
GetPrimaryFrame() const128 nsIFrame* InputType::GetPrimaryFrame() const {
129 return mInputElement->GetPrimaryFrame();
130 }
131
DropReference()132 void InputType::DropReference() {
133 // Drop our (non ref-counted) reference.
134 mInputElement = nullptr;
135 }
136
IsTooLong() const137 bool InputType::IsTooLong() const { return false; }
138
IsTooShort() const139 bool InputType::IsTooShort() const { return false; }
140
IsValueMissing() const141 bool InputType::IsValueMissing() const { return false; }
142
HasTypeMismatch() const143 bool InputType::HasTypeMismatch() const { return false; }
144
HasPatternMismatch() const145 Maybe<bool> InputType::HasPatternMismatch() const { return Some(false); }
146
IsRangeOverflow() const147 bool InputType::IsRangeOverflow() const { return false; }
148
IsRangeUnderflow() const149 bool InputType::IsRangeUnderflow() const { return false; }
150
HasStepMismatch(bool aUseZeroIfValueNaN) const151 bool InputType::HasStepMismatch(bool aUseZeroIfValueNaN) const { return false; }
152
HasBadInput() const153 bool InputType::HasBadInput() const { return false; }
154
GetValidationMessage(nsAString & aValidationMessage,nsIConstraintValidation::ValidityStateType aType)155 nsresult InputType::GetValidationMessage(
156 nsAString& aValidationMessage,
157 nsIConstraintValidation::ValidityStateType aType) {
158 aValidationMessage.Truncate();
159
160 switch (aType) {
161 case nsIConstraintValidation::VALIDITY_STATE_TOO_LONG: {
162 int32_t maxLength = mInputElement->MaxLength();
163 int32_t textLength = mInputElement->InputTextLength(CallerType::System);
164 nsAutoString strMaxLength;
165 nsAutoString strTextLength;
166
167 strMaxLength.AppendInt(maxLength);
168 strTextLength.AppendInt(textLength);
169
170 return nsContentUtils::FormatMaybeLocalizedString(
171 aValidationMessage, nsContentUtils::eDOM_PROPERTIES,
172 "FormValidationTextTooLong", mInputElement->OwnerDoc(), strMaxLength,
173 strTextLength);
174 }
175 case nsIConstraintValidation::VALIDITY_STATE_TOO_SHORT: {
176 int32_t minLength = mInputElement->MinLength();
177 int32_t textLength = mInputElement->InputTextLength(CallerType::System);
178 nsAutoString strMinLength;
179 nsAutoString strTextLength;
180
181 strMinLength.AppendInt(minLength);
182 strTextLength.AppendInt(textLength);
183
184 return nsContentUtils::FormatMaybeLocalizedString(
185 aValidationMessage, nsContentUtils::eDOM_PROPERTIES,
186 "FormValidationTextTooShort", mInputElement->OwnerDoc(), strMinLength,
187 strTextLength);
188 }
189 case nsIConstraintValidation::VALIDITY_STATE_VALUE_MISSING:
190 return GetValueMissingMessage(aValidationMessage);
191 case nsIConstraintValidation::VALIDITY_STATE_TYPE_MISMATCH: {
192 return GetTypeMismatchMessage(aValidationMessage);
193 }
194 case nsIConstraintValidation::VALIDITY_STATE_PATTERN_MISMATCH: {
195 nsAutoString title;
196 mInputElement->GetAttr(nsGkAtoms::title, title);
197
198 if (title.IsEmpty()) {
199 return nsContentUtils::GetMaybeLocalizedString(
200 nsContentUtils::eDOM_PROPERTIES, "FormValidationPatternMismatch",
201 mInputElement->OwnerDoc(), aValidationMessage);
202 }
203
204 if (title.Length() >
205 nsIConstraintValidation::sContentSpecifiedMaxLengthMessage) {
206 title.Truncate(
207 nsIConstraintValidation::sContentSpecifiedMaxLengthMessage);
208 }
209 return nsContentUtils::FormatMaybeLocalizedString(
210 aValidationMessage, nsContentUtils::eDOM_PROPERTIES,
211 "FormValidationPatternMismatchWithTitle", mInputElement->OwnerDoc(),
212 title);
213 }
214 case nsIConstraintValidation::VALIDITY_STATE_RANGE_OVERFLOW:
215 return GetRangeOverflowMessage(aValidationMessage);
216 case nsIConstraintValidation::VALIDITY_STATE_RANGE_UNDERFLOW:
217 return GetRangeUnderflowMessage(aValidationMessage);
218 case nsIConstraintValidation::VALIDITY_STATE_STEP_MISMATCH: {
219 Decimal value = mInputElement->GetValueAsDecimal();
220 if (MOZ_UNLIKELY(NS_WARN_IF(value.isNaN()))) {
221 // TODO(bug 1651070): This should ideally never happen, but we don't
222 // deal with lang changes correctly, so it could.
223 return GetBadInputMessage(aValidationMessage);
224 }
225
226 Decimal step = mInputElement->GetStep();
227 MOZ_ASSERT(step != kStepAny && step > Decimal(0));
228
229 Decimal stepBase = mInputElement->GetStepBase();
230
231 Decimal valueLow = value - NS_floorModulo(value - stepBase, step);
232 Decimal valueHigh = value + step - NS_floorModulo(value - stepBase, step);
233
234 Decimal maximum = mInputElement->GetMaximum();
235
236 if (maximum.isNaN() || valueHigh <= maximum) {
237 nsAutoString valueLowStr, valueHighStr;
238 ConvertNumberToString(valueLow, valueLowStr);
239 ConvertNumberToString(valueHigh, valueHighStr);
240
241 if (valueLowStr.Equals(valueHighStr)) {
242 return nsContentUtils::FormatMaybeLocalizedString(
243 aValidationMessage, nsContentUtils::eDOM_PROPERTIES,
244 "FormValidationStepMismatchOneValue", mInputElement->OwnerDoc(),
245 valueLowStr);
246 }
247 return nsContentUtils::FormatMaybeLocalizedString(
248 aValidationMessage, nsContentUtils::eDOM_PROPERTIES,
249 "FormValidationStepMismatch", mInputElement->OwnerDoc(),
250 valueLowStr, valueHighStr);
251 }
252
253 nsAutoString valueLowStr;
254 ConvertNumberToString(valueLow, valueLowStr);
255
256 return nsContentUtils::FormatMaybeLocalizedString(
257 aValidationMessage, nsContentUtils::eDOM_PROPERTIES,
258 "FormValidationStepMismatchOneValue", mInputElement->OwnerDoc(),
259 valueLowStr);
260 }
261 case nsIConstraintValidation::VALIDITY_STATE_BAD_INPUT:
262 return GetBadInputMessage(aValidationMessage);
263 default:
264 MOZ_ASSERT_UNREACHABLE("Unknown validity state");
265 return NS_ERROR_UNEXPECTED;
266 }
267 }
268
GetValueMissingMessage(nsAString & aMessage)269 nsresult InputType::GetValueMissingMessage(nsAString& aMessage) {
270 return nsContentUtils::GetMaybeLocalizedString(
271 nsContentUtils::eDOM_PROPERTIES, "FormValidationValueMissing",
272 mInputElement->OwnerDoc(), aMessage);
273 }
274
GetTypeMismatchMessage(nsAString & aMessage)275 nsresult InputType::GetTypeMismatchMessage(nsAString& aMessage) {
276 return NS_ERROR_UNEXPECTED;
277 }
278
GetRangeOverflowMessage(nsAString & aMessage)279 nsresult InputType::GetRangeOverflowMessage(nsAString& aMessage) {
280 return NS_ERROR_UNEXPECTED;
281 }
282
GetRangeUnderflowMessage(nsAString & aMessage)283 nsresult InputType::GetRangeUnderflowMessage(nsAString& aMessage) {
284 return NS_ERROR_UNEXPECTED;
285 }
286
GetBadInputMessage(nsAString & aMessage)287 nsresult InputType::GetBadInputMessage(nsAString& aMessage) {
288 return NS_ERROR_UNEXPECTED;
289 }
290
MinMaxStepAttrChanged()291 nsresult InputType::MinMaxStepAttrChanged() { return NS_OK; }
292
ConvertStringToNumber(nsAString & aValue,Decimal & aResultValue) const293 bool InputType::ConvertStringToNumber(nsAString& aValue,
294 Decimal& aResultValue) const {
295 NS_WARNING("InputType::ConvertStringToNumber called");
296
297 return false;
298 }
299
ConvertNumberToString(Decimal aValue,nsAString & aResultString) const300 bool InputType::ConvertNumberToString(Decimal aValue,
301 nsAString& aResultString) const {
302 NS_WARNING("InputType::ConvertNumberToString called");
303
304 return false;
305 }
306
ParseDate(const nsAString & aValue,uint32_t * aYear,uint32_t * aMonth,uint32_t * aDay) const307 bool InputType::ParseDate(const nsAString& aValue, uint32_t* aYear,
308 uint32_t* aMonth, uint32_t* aDay) const {
309 // TODO: move this function and implementation to DateTimeInpuTypeBase when
310 // refactoring is completed. Now we can only call HTMLInputElement::ParseDate
311 // from here, since the method is protected and only InputType is a friend
312 // class.
313 return mInputElement->ParseDate(aValue, aYear, aMonth, aDay);
314 }
315
ParseTime(const nsAString & aValue,uint32_t * aResult) const316 bool InputType::ParseTime(const nsAString& aValue, uint32_t* aResult) const {
317 // see comment in InputType::ParseDate().
318 return HTMLInputElement::ParseTime(aValue, aResult);
319 }
320
ParseMonth(const nsAString & aValue,uint32_t * aYear,uint32_t * aMonth) const321 bool InputType::ParseMonth(const nsAString& aValue, uint32_t* aYear,
322 uint32_t* aMonth) const {
323 // see comment in InputType::ParseDate().
324 return mInputElement->ParseMonth(aValue, aYear, aMonth);
325 }
326
ParseWeek(const nsAString & aValue,uint32_t * aYear,uint32_t * aWeek) const327 bool InputType::ParseWeek(const nsAString& aValue, uint32_t* aYear,
328 uint32_t* aWeek) const {
329 // see comment in InputType::ParseDate().
330 return mInputElement->ParseWeek(aValue, aYear, aWeek);
331 }
332
ParseDateTimeLocal(const nsAString & aValue,uint32_t * aYear,uint32_t * aMonth,uint32_t * aDay,uint32_t * aTime) const333 bool InputType::ParseDateTimeLocal(const nsAString& aValue, uint32_t* aYear,
334 uint32_t* aMonth, uint32_t* aDay,
335 uint32_t* aTime) const {
336 // see comment in InputType::ParseDate().
337 return mInputElement->ParseDateTimeLocal(aValue, aYear, aMonth, aDay, aTime);
338 }
339
MonthsSinceJan1970(uint32_t aYear,uint32_t aMonth) const340 int32_t InputType::MonthsSinceJan1970(uint32_t aYear, uint32_t aMonth) const {
341 // see comment in InputType::ParseDate().
342 return mInputElement->MonthsSinceJan1970(aYear, aMonth);
343 }
344
DaysSinceEpochFromWeek(uint32_t aYear,uint32_t aWeek) const345 double InputType::DaysSinceEpochFromWeek(uint32_t aYear, uint32_t aWeek) const {
346 // see comment in InputType::ParseDate().
347 return mInputElement->DaysSinceEpochFromWeek(aYear, aWeek);
348 }
349
DayOfWeek(uint32_t aYear,uint32_t aMonth,uint32_t aDay,bool isoWeek) const350 uint32_t InputType::DayOfWeek(uint32_t aYear, uint32_t aMonth, uint32_t aDay,
351 bool isoWeek) const {
352 // see comment in InputType::ParseDate().
353 return mInputElement->DayOfWeek(aYear, aMonth, aDay, isoWeek);
354 }
355
MaximumWeekInYear(uint32_t aYear) const356 uint32_t InputType::MaximumWeekInYear(uint32_t aYear) const {
357 // see comment in InputType::ParseDate().
358 return mInputElement->MaximumWeekInYear(aYear);
359 }
360