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