/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "mozilla/dom/InputType.h" #include "mozilla/Assertions.h" #include "mozilla/Likely.h" #include "nsIFormControl.h" #include "mozilla/dom/ButtonInputTypes.h" #include "mozilla/dom/CheckableInputTypes.h" #include "mozilla/dom/ColorInputType.h" #include "mozilla/dom/DateTimeInputTypes.h" #include "mozilla/dom/FileInputType.h" #include "mozilla/dom/HiddenInputType.h" #include "mozilla/dom/HTMLInputElement.h" #include "mozilla/dom/NumericInputTypes.h" #include "mozilla/dom/SingleLineTextInputTypes.h" #include "nsContentUtils.h" using namespace mozilla; using namespace mozilla::dom; const Decimal InputType::kStepAny = Decimal(0); /* static */ UniquePtr InputType::Create( HTMLInputElement* aInputElement, FormControlType aType, void* aMemory) { UniquePtr inputType; switch (aType) { // Single line text case FormControlType::InputText: inputType.reset(TextInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputTel: inputType.reset(TelInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputEmail: inputType.reset(EmailInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputSearch: inputType.reset(SearchInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputPassword: inputType.reset(PasswordInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputUrl: inputType.reset(URLInputType::Create(aInputElement, aMemory)); break; // Button case FormControlType::InputButton: inputType.reset(ButtonInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputSubmit: inputType.reset(SubmitInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputImage: inputType.reset(ImageInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputReset: inputType.reset(ResetInputType::Create(aInputElement, aMemory)); break; // Checkable case FormControlType::InputCheckbox: inputType.reset(CheckboxInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputRadio: inputType.reset(RadioInputType::Create(aInputElement, aMemory)); break; // Numeric case FormControlType::InputNumber: inputType.reset(NumberInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputRange: inputType.reset(RangeInputType::Create(aInputElement, aMemory)); break; // DateTime case FormControlType::InputDate: inputType.reset(DateInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputTime: inputType.reset(TimeInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputMonth: inputType.reset(MonthInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputWeek: inputType.reset(WeekInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputDatetimeLocal: inputType.reset(DateTimeLocalInputType::Create(aInputElement, aMemory)); break; // Others case FormControlType::InputColor: inputType.reset(ColorInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputFile: inputType.reset(FileInputType::Create(aInputElement, aMemory)); break; case FormControlType::InputHidden: inputType.reset(HiddenInputType::Create(aInputElement, aMemory)); break; default: inputType.reset(TextInputType::Create(aInputElement, aMemory)); } return inputType; } bool InputType::IsMutable() const { return !mInputElement->IsDisabled(); } bool InputType::IsValueEmpty() const { return mInputElement->IsValueEmpty(); } void InputType::GetNonFileValueInternal(nsAString& aValue) const { return mInputElement->GetNonFileValueInternal(aValue); } nsresult InputType::SetValueInternal(const nsAString& aValue, const ValueSetterOptions& aOptions) { RefPtr inputElement(mInputElement); return inputElement->SetValueInternal(aValue, aOptions); } nsIFrame* InputType::GetPrimaryFrame() const { return mInputElement->GetPrimaryFrame(); } void InputType::DropReference() { // Drop our (non ref-counted) reference. mInputElement = nullptr; } bool InputType::IsTooLong() const { return false; } bool InputType::IsTooShort() const { return false; } bool InputType::IsValueMissing() const { return false; } bool InputType::HasTypeMismatch() const { return false; } Maybe InputType::HasPatternMismatch() const { return Some(false); } bool InputType::IsRangeOverflow() const { return false; } bool InputType::IsRangeUnderflow() const { return false; } bool InputType::HasStepMismatch() const { return false; } bool InputType::HasBadInput() const { return false; } nsresult InputType::GetValidationMessage( nsAString& aValidationMessage, nsIConstraintValidation::ValidityStateType aType) { aValidationMessage.Truncate(); switch (aType) { case nsIConstraintValidation::VALIDITY_STATE_TOO_LONG: { int32_t maxLength = mInputElement->MaxLength(); int32_t textLength = mInputElement->InputTextLength(CallerType::System); nsAutoString strMaxLength; nsAutoString strTextLength; strMaxLength.AppendInt(maxLength); strTextLength.AppendInt(textLength); return nsContentUtils::FormatMaybeLocalizedString( aValidationMessage, nsContentUtils::eDOM_PROPERTIES, "FormValidationTextTooLong", mInputElement->OwnerDoc(), strMaxLength, strTextLength); } case nsIConstraintValidation::VALIDITY_STATE_TOO_SHORT: { int32_t minLength = mInputElement->MinLength(); int32_t textLength = mInputElement->InputTextLength(CallerType::System); nsAutoString strMinLength; nsAutoString strTextLength; strMinLength.AppendInt(minLength); strTextLength.AppendInt(textLength); return nsContentUtils::FormatMaybeLocalizedString( aValidationMessage, nsContentUtils::eDOM_PROPERTIES, "FormValidationTextTooShort", mInputElement->OwnerDoc(), strMinLength, strTextLength); } case nsIConstraintValidation::VALIDITY_STATE_VALUE_MISSING: return GetValueMissingMessage(aValidationMessage); case nsIConstraintValidation::VALIDITY_STATE_TYPE_MISMATCH: { return GetTypeMismatchMessage(aValidationMessage); } case nsIConstraintValidation::VALIDITY_STATE_PATTERN_MISMATCH: { nsAutoString title; mInputElement->GetAttr(nsGkAtoms::title, title); if (title.IsEmpty()) { return nsContentUtils::GetMaybeLocalizedString( nsContentUtils::eDOM_PROPERTIES, "FormValidationPatternMismatch", mInputElement->OwnerDoc(), aValidationMessage); } if (title.Length() > nsIConstraintValidation::sContentSpecifiedMaxLengthMessage) { title.Truncate( nsIConstraintValidation::sContentSpecifiedMaxLengthMessage); } return nsContentUtils::FormatMaybeLocalizedString( aValidationMessage, nsContentUtils::eDOM_PROPERTIES, "FormValidationPatternMismatchWithTitle", mInputElement->OwnerDoc(), title); } case nsIConstraintValidation::VALIDITY_STATE_RANGE_OVERFLOW: return GetRangeOverflowMessage(aValidationMessage); case nsIConstraintValidation::VALIDITY_STATE_RANGE_UNDERFLOW: return GetRangeUnderflowMessage(aValidationMessage); case nsIConstraintValidation::VALIDITY_STATE_STEP_MISMATCH: { Decimal value = mInputElement->GetValueAsDecimal(); if (MOZ_UNLIKELY(NS_WARN_IF(value.isNaN()))) { // TODO(bug 1651070): This should ideally never happen, but we don't // deal with lang changes correctly, so it could. return GetBadInputMessage(aValidationMessage); } Decimal step = mInputElement->GetStep(); MOZ_ASSERT(step != kStepAny && step > Decimal(0)); Decimal stepBase = mInputElement->GetStepBase(); Decimal valueLow = value - NS_floorModulo(value - stepBase, step); Decimal valueHigh = value + step - NS_floorModulo(value - stepBase, step); Decimal maximum = mInputElement->GetMaximum(); if (maximum.isNaN() || valueHigh <= maximum) { nsAutoString valueLowStr, valueHighStr; ConvertNumberToString(valueLow, valueLowStr); ConvertNumberToString(valueHigh, valueHighStr); if (valueLowStr.Equals(valueHighStr)) { return nsContentUtils::FormatMaybeLocalizedString( aValidationMessage, nsContentUtils::eDOM_PROPERTIES, "FormValidationStepMismatchOneValue", mInputElement->OwnerDoc(), valueLowStr); } return nsContentUtils::FormatMaybeLocalizedString( aValidationMessage, nsContentUtils::eDOM_PROPERTIES, "FormValidationStepMismatch", mInputElement->OwnerDoc(), valueLowStr, valueHighStr); } nsAutoString valueLowStr; ConvertNumberToString(valueLow, valueLowStr); return nsContentUtils::FormatMaybeLocalizedString( aValidationMessage, nsContentUtils::eDOM_PROPERTIES, "FormValidationStepMismatchOneValue", mInputElement->OwnerDoc(), valueLowStr); } case nsIConstraintValidation::VALIDITY_STATE_BAD_INPUT: return GetBadInputMessage(aValidationMessage); default: MOZ_ASSERT_UNREACHABLE("Unknown validity state"); return NS_ERROR_UNEXPECTED; } } nsresult InputType::GetValueMissingMessage(nsAString& aMessage) { return nsContentUtils::GetMaybeLocalizedString( nsContentUtils::eDOM_PROPERTIES, "FormValidationValueMissing", mInputElement->OwnerDoc(), aMessage); } nsresult InputType::GetTypeMismatchMessage(nsAString& aMessage) { return NS_ERROR_UNEXPECTED; } nsresult InputType::GetRangeOverflowMessage(nsAString& aMessage) { return NS_ERROR_UNEXPECTED; } nsresult InputType::GetRangeUnderflowMessage(nsAString& aMessage) { return NS_ERROR_UNEXPECTED; } nsresult InputType::GetBadInputMessage(nsAString& aMessage) { return NS_ERROR_UNEXPECTED; } bool InputType::ConvertStringToNumber(nsAString& aValue, Decimal& aResultValue) const { NS_WARNING("InputType::ConvertStringToNumber called"); return false; } bool InputType::ConvertNumberToString(Decimal aValue, nsAString& aResultString) const { NS_WARNING("InputType::ConvertNumberToString called"); return false; } bool InputType::ParseDate(const nsAString& aValue, uint32_t* aYear, uint32_t* aMonth, uint32_t* aDay) const { // TODO: move this function and implementation to DateTimeInpuTypeBase when // refactoring is completed. Now we can only call HTMLInputElement::ParseDate // from here, since the method is protected and only InputType is a friend // class. return mInputElement->ParseDate(aValue, aYear, aMonth, aDay); } bool InputType::ParseTime(const nsAString& aValue, uint32_t* aResult) const { // see comment in InputType::ParseDate(). return HTMLInputElement::ParseTime(aValue, aResult); } bool InputType::ParseMonth(const nsAString& aValue, uint32_t* aYear, uint32_t* aMonth) const { // see comment in InputType::ParseDate(). return mInputElement->ParseMonth(aValue, aYear, aMonth); } bool InputType::ParseWeek(const nsAString& aValue, uint32_t* aYear, uint32_t* aWeek) const { // see comment in InputType::ParseDate(). return mInputElement->ParseWeek(aValue, aYear, aWeek); } bool InputType::ParseDateTimeLocal(const nsAString& aValue, uint32_t* aYear, uint32_t* aMonth, uint32_t* aDay, uint32_t* aTime) const { // see comment in InputType::ParseDate(). return mInputElement->ParseDateTimeLocal(aValue, aYear, aMonth, aDay, aTime); } int32_t InputType::MonthsSinceJan1970(uint32_t aYear, uint32_t aMonth) const { // see comment in InputType::ParseDate(). return mInputElement->MonthsSinceJan1970(aYear, aMonth); } double InputType::DaysSinceEpochFromWeek(uint32_t aYear, uint32_t aWeek) const { // see comment in InputType::ParseDate(). return mInputElement->DaysSinceEpochFromWeek(aYear, aWeek); } uint32_t InputType::DayOfWeek(uint32_t aYear, uint32_t aMonth, uint32_t aDay, bool isoWeek) const { // see comment in InputType::ParseDate(). return mInputElement->DayOfWeek(aYear, aMonth, aDay, isoWeek); } uint32_t InputType::MaximumWeekInYear(uint32_t aYear) const { // see comment in InputType::ParseDate(). return mInputElement->MaximumWeekInYear(aYear); }