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 "mozilla/TextControlState.h"
10 #include "mozilla/dom/HTMLInputElement.h"
11 #include "ICUUtils.h"
12
13 using namespace mozilla;
14 using namespace mozilla::dom;
15
IsRangeOverflow() const16 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
IsRangeUnderflow() const30 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
HasStepMismatch(bool aUseZeroIfValueNaN) const44 bool NumericInputTypeBase::HasStepMismatch(bool aUseZeroIfValueNaN) const {
45 Decimal value = mInputElement->GetValueAsDecimal();
46 if (value.isNaN()) {
47 if (aUseZeroIfValueNaN) {
48 value = Decimal(0);
49 } else {
50 // The element can't suffer from step mismatch if it's value isn't a
51 // number.
52 return false;
53 }
54 }
55
56 Decimal step = mInputElement->GetStep();
57 if (step == kStepAny) {
58 return false;
59 }
60
61 // Value has to be an integral multiple of step.
62 return NS_floorModulo(value - GetStepBase(), step) != Decimal(0);
63 }
64
GetRangeOverflowMessage(nsAString & aMessage)65 nsresult NumericInputTypeBase::GetRangeOverflowMessage(nsAString& aMessage) {
66 // We want to show the value as parsed when it's a number
67 Decimal maximum = mInputElement->GetMaximum();
68 MOZ_ASSERT(!maximum.isNaN());
69
70 nsAutoString maxStr;
71 char buf[32];
72 DebugOnly<bool> ok = maximum.toString(buf, ArrayLength(buf));
73 maxStr.AssignASCII(buf);
74 MOZ_ASSERT(ok, "buf not big enough");
75
76 return nsContentUtils::FormatMaybeLocalizedString(
77 aMessage, nsContentUtils::eDOM_PROPERTIES,
78 "FormValidationNumberRangeOverflow", mInputElement->OwnerDoc(), maxStr);
79 }
80
GetRangeUnderflowMessage(nsAString & aMessage)81 nsresult NumericInputTypeBase::GetRangeUnderflowMessage(nsAString& aMessage) {
82 Decimal minimum = mInputElement->GetMinimum();
83 MOZ_ASSERT(!minimum.isNaN());
84
85 nsAutoString minStr;
86 char buf[32];
87 DebugOnly<bool> ok = minimum.toString(buf, ArrayLength(buf));
88 minStr.AssignASCII(buf);
89 MOZ_ASSERT(ok, "buf not big enough");
90
91 return nsContentUtils::FormatMaybeLocalizedString(
92 aMessage, nsContentUtils::eDOM_PROPERTIES,
93 "FormValidationNumberRangeUnderflow", mInputElement->OwnerDoc(), minStr);
94 }
95
ConvertStringToNumber(nsAString & aValue,Decimal & aResultValue) const96 bool NumericInputTypeBase::ConvertStringToNumber(nsAString& aValue,
97 Decimal& aResultValue) const {
98 aResultValue = HTMLInputElement::StringToDecimal(aValue);
99 return aResultValue.isFinite();
100 }
101
ConvertNumberToString(Decimal aValue,nsAString & aResultString) const102 bool NumericInputTypeBase::ConvertNumberToString(
103 Decimal aValue, nsAString& aResultString) const {
104 MOZ_ASSERT(aValue.isFinite(), "aValue must be a valid non-Infinite number.");
105
106 aResultString.Truncate();
107
108 char buf[32];
109 bool ok = aValue.toString(buf, ArrayLength(buf));
110 aResultString.AssignASCII(buf);
111 MOZ_ASSERT(ok, "buf not big enough");
112
113 return ok;
114 }
115
116 /* input type=number */
117
IsValueMissing() const118 bool NumberInputType::IsValueMissing() const {
119 if (!mInputElement->IsRequired()) {
120 return false;
121 }
122
123 if (!IsMutable()) {
124 return false;
125 }
126
127 return IsValueEmpty();
128 }
129
HasBadInput() const130 bool NumberInputType::HasBadInput() const {
131 nsAutoString value;
132 GetNonFileValueInternal(value);
133 return !value.IsEmpty() && mInputElement->GetValueAsDecimal().isNaN();
134 }
135
ConvertStringToNumber(nsAString & aValue,Decimal & aResultValue) const136 bool NumberInputType::ConvertStringToNumber(nsAString& aValue,
137 Decimal& aResultValue) const {
138 ICUUtils::LanguageTagIterForContent langTagIter(mInputElement);
139 aResultValue =
140 Decimal::fromDouble(ICUUtils::ParseNumber(aValue, langTagIter));
141 if (aResultValue.isFinite()) {
142 return true;
143 }
144 return NumericInputTypeBase::ConvertStringToNumber(aValue, aResultValue);
145 }
146
ConvertNumberToString(Decimal aValue,nsAString & aResultString) const147 bool NumberInputType::ConvertNumberToString(Decimal aValue,
148 nsAString& aResultString) const {
149 MOZ_ASSERT(aValue.isFinite(), "aValue must be a valid non-Infinite number.");
150
151 aResultString.Truncate();
152 ICUUtils::LanguageTagIterForContent langTagIter(mInputElement);
153 ICUUtils::LocalizeNumber(aValue.toDouble(), langTagIter, aResultString);
154 return true;
155 }
156
GetValueMissingMessage(nsAString & aMessage)157 nsresult NumberInputType::GetValueMissingMessage(nsAString& aMessage) {
158 return nsContentUtils::GetMaybeLocalizedString(
159 nsContentUtils::eDOM_PROPERTIES, "FormValidationBadInputNumber",
160 mInputElement->OwnerDoc(), aMessage);
161 }
162
GetBadInputMessage(nsAString & aMessage)163 nsresult NumberInputType::GetBadInputMessage(nsAString& aMessage) {
164 return nsContentUtils::GetMaybeLocalizedString(
165 nsContentUtils::eDOM_PROPERTIES, "FormValidationBadInputNumber",
166 mInputElement->OwnerDoc(), aMessage);
167 }
168
IsMutable() const169 bool NumberInputType::IsMutable() const {
170 return !mInputElement->IsDisabled() &&
171 !mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly);
172 }
173
174 /* input type=range */
MinMaxStepAttrChanged()175 nsresult RangeInputType::MinMaxStepAttrChanged() {
176 // The value may need to change when @min/max/step changes since the value may
177 // have been invalid and can now change to a valid value, or vice versa. For
178 // example, consider: <input type=range value=-1 max=1 step=3>. The valid
179 // range is 0 to 1 while the nearest valid steps are -1 and 2 (the max value
180 // having prevented there being a valid step in range). Changing @max to/from
181 // 1 and a number greater than on equal to 3 should change whether we have a
182 // step mismatch or not.
183 // The value may also need to change between a value that results in a step
184 // mismatch and a value that results in overflow. For example, if @max in the
185 // example above were to change from 1 to -1.
186 nsAutoString value;
187 GetNonFileValueInternal(value);
188 return SetValueInternal(value,
189 TextControlState::ValueSetterOption::ByInternalAPI);
190 }
191