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