NumericInputTypes.cpp (5640B)
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/NumericInputTypes.h" 8 9 #include "ICUUtils.h" 10 #include "mozilla/TextControlState.h" 11 #include "mozilla/dom/HTMLInputElement.h" 12 13 using namespace mozilla; 14 using namespace mozilla::dom; 15 16 bool NumericInputTypeBase::IsRangeOverflow() const { 17 Decimal maximum = mInputElement->GetMaximum(); 18 if (maximum.isNaN()) { 19 return false; 20 } 21 22 Decimal value = mInputElement->GetValueAsDecimal(); 23 if (value.isNaN()) { 24 return false; 25 } 26 27 return value > maximum; 28 } 29 30 bool NumericInputTypeBase::IsRangeUnderflow() const { 31 Decimal minimum = mInputElement->GetMinimum(); 32 if (minimum.isNaN()) { 33 return false; 34 } 35 36 Decimal value = mInputElement->GetValueAsDecimal(); 37 if (value.isNaN()) { 38 return false; 39 } 40 41 return value < minimum; 42 } 43 44 bool NumericInputTypeBase::HasStepMismatch() const { 45 Decimal value = mInputElement->GetValueAsDecimal(); 46 return mInputElement->ValueIsStepMismatch(value); 47 } 48 49 nsresult NumericInputTypeBase::GetRangeOverflowMessage(nsAString& aMessage) { 50 // We want to show the value as parsed when it's a number 51 Decimal maximum = mInputElement->GetMaximum(); 52 MOZ_ASSERT(!maximum.isNaN()); 53 54 nsAutoString maxStr; 55 ConvertNumberToString(maximum, Localized::Yes, maxStr); 56 return nsContentUtils::FormatMaybeLocalizedString( 57 aMessage, nsContentUtils::eDOM_PROPERTIES, 58 "FormValidationNumberRangeOverflow", mInputElement->OwnerDoc(), maxStr); 59 } 60 61 nsresult NumericInputTypeBase::GetRangeUnderflowMessage(nsAString& aMessage) { 62 Decimal minimum = mInputElement->GetMinimum(); 63 MOZ_ASSERT(!minimum.isNaN()); 64 65 nsAutoString minStr; 66 ConvertNumberToString(minimum, Localized::Yes, minStr); 67 return nsContentUtils::FormatMaybeLocalizedString( 68 aMessage, nsContentUtils::eDOM_PROPERTIES, 69 "FormValidationNumberRangeUnderflow", mInputElement->OwnerDoc(), minStr); 70 } 71 72 auto NumericInputTypeBase::ConvertStringToNumber(const nsAString& aValue) const 73 -> StringToNumberResult { 74 return {HTMLInputElement::StringToDecimal(aValue)}; 75 } 76 77 bool NumericInputTypeBase::ConvertNumberToString( 78 Decimal aValue, Localized, nsAString& aResultString) const { 79 MOZ_ASSERT(aValue.isFinite(), "aValue must be a valid non-Infinite number."); 80 aResultString.Truncate(); 81 aResultString.AssignASCII(aValue.toString().c_str()); 82 return true; 83 } 84 85 /* input type=number */ 86 87 bool NumberInputType::IsValueMissing() const { 88 if (!mInputElement->IsRequired()) { 89 return false; 90 } 91 92 if (!IsMutable()) { 93 return false; 94 } 95 96 return IsValueEmpty(); 97 } 98 99 bool NumberInputType::HasBadInput() const { 100 nsAutoString value; 101 GetNonFileValueInternal(value); 102 return !value.IsEmpty() && mInputElement->GetValueAsDecimal().isNaN(); 103 } 104 105 auto NumberInputType::ConvertStringToNumber(const nsAString& aValue) const 106 -> StringToNumberResult { 107 auto result = NumericInputTypeBase::ConvertStringToNumber(aValue); 108 if (result.mResult.isFinite()) { 109 return result; 110 } 111 // Try to read the localized value from the user. 112 ICUUtils::LanguageTagIterForContent langTagIter(mInputElement); 113 result.mLocalized = true; 114 result.mResult = 115 Decimal::fromDouble(ICUUtils::ParseNumber(aValue, langTagIter)); 116 return result; 117 } 118 119 bool NumberInputType::ConvertNumberToString(Decimal aValue, 120 Localized aLocalized, 121 nsAString& aResultString) const { 122 MOZ_ASSERT(aValue.isFinite(), "aValue must be a valid non-Infinite number."); 123 124 if (aLocalized == Localized::No) { 125 return NumericInputTypeBase::ConvertNumberToString(aValue, aLocalized, 126 aResultString); 127 } 128 aResultString.Truncate(); 129 ICUUtils::LanguageTagIterForContent langTagIter(mInputElement); 130 ICUUtils::LocalizeNumber(aValue.toDouble(), langTagIter, aResultString); 131 return true; 132 } 133 134 nsresult NumberInputType::GetValueMissingMessage(nsAString& aMessage) { 135 return nsContentUtils::GetMaybeLocalizedString( 136 nsContentUtils::eDOM_PROPERTIES, "FormValidationBadInputNumber", 137 mInputElement->OwnerDoc(), aMessage); 138 } 139 140 nsresult NumberInputType::GetBadInputMessage(nsAString& aMessage) { 141 return nsContentUtils::GetMaybeLocalizedString( 142 nsContentUtils::eDOM_PROPERTIES, "FormValidationBadInputNumber", 143 mInputElement->OwnerDoc(), aMessage); 144 } 145 146 bool NumberInputType::IsMutable() const { 147 return !mInputElement->IsDisabledOrReadOnly(); 148 } 149 150 /* input type=range */ 151 void RangeInputType::MinMaxStepAttrChanged() { 152 // The value may need to change when @min/max/step changes since the value may 153 // have been invalid and can now change to a valid value, or vice versa. For 154 // example, consider: <input type=range value=-1 max=1 step=3>. The valid 155 // range is 0 to 1 while the nearest valid steps are -1 and 2 (the max value 156 // having prevented there being a valid step in range). Changing @max to/from 157 // 1 and a number greater than on equal to 3 should change whether we have a 158 // step mismatch or not. 159 // The value may also need to change between a value that results in a step 160 // mismatch and a value that results in overflow. For example, if @max in the 161 // example above were to change from 1 to -1. 162 nsAutoString value; 163 GetNonFileValueInternal(value); 164 SetValueInternal(value, TextControlState::ValueSetterOption::ByInternalAPI); 165 }