1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "config.h"
33 #include "NumberInputType.h"
34
35 #include "BeforeTextInsertedEvent.h"
36 #include "ExceptionCode.h"
37 #include "HTMLInputElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLParserIdioms.h"
40 #include "KeyboardEvent.h"
41 #include "LocalizedNumber.h"
42 #include "RenderTextControl.h"
43 #include <limits>
44 #include <wtf/ASCIICType.h>
45 #include <wtf/MathExtras.h>
46 #include <wtf/PassOwnPtr.h>
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51 using namespace std;
52
53 static const double numberDefaultStep = 1.0;
54 static const double numberStepScaleFactor = 1.0;
55
create(HTMLInputElement * element)56 PassOwnPtr<InputType> NumberInputType::create(HTMLInputElement* element)
57 {
58 return adoptPtr(new NumberInputType(element));
59 }
60
formControlType() const61 const AtomicString& NumberInputType::formControlType() const
62 {
63 return InputTypeNames::number();
64 }
65
valueAsNumber() const66 double NumberInputType::valueAsNumber() const
67 {
68 return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN());
69 }
70
setValueAsNumber(double newValue,ExceptionCode & ec) const71 void NumberInputType::setValueAsNumber(double newValue, ExceptionCode& ec) const
72 {
73 if (newValue < -numeric_limits<float>::max()) {
74 ec = INVALID_STATE_ERR;
75 return;
76 }
77 if (newValue > numeric_limits<float>::max()) {
78 ec = INVALID_STATE_ERR;
79 return;
80 }
81 element()->setValue(serialize(newValue));
82 }
83
typeMismatchFor(const String & value) const84 bool NumberInputType::typeMismatchFor(const String& value) const
85 {
86 return !value.isEmpty() && !parseToDoubleForNumberType(value, 0);
87 }
88
typeMismatch() const89 bool NumberInputType::typeMismatch() const
90 {
91 ASSERT(!typeMismatchFor(element()->value()));
92 return false;
93 }
94
rangeUnderflow(const String & value) const95 bool NumberInputType::rangeUnderflow(const String& value) const
96 {
97 const double nan = numeric_limits<double>::quiet_NaN();
98 double doubleValue = parseToDouble(value, nan);
99 return isfinite(doubleValue) && doubleValue < minimum();
100 }
101
rangeOverflow(const String & value) const102 bool NumberInputType::rangeOverflow(const String& value) const
103 {
104 const double nan = numeric_limits<double>::quiet_NaN();
105 double doubleValue = parseToDouble(value, nan);
106 return isfinite(doubleValue) && doubleValue > maximum();
107 }
108
supportsRangeLimitation() const109 bool NumberInputType::supportsRangeLimitation() const
110 {
111 return true;
112 }
113
minimum() const114 double NumberInputType::minimum() const
115 {
116 return parseToDouble(element()->fastGetAttribute(minAttr), -numeric_limits<float>::max());
117 }
118
maximum() const119 double NumberInputType::maximum() const
120 {
121 return parseToDouble(element()->fastGetAttribute(maxAttr), numeric_limits<float>::max());
122 }
123
isSteppable() const124 bool NumberInputType::isSteppable() const
125 {
126 return true;
127 }
128
stepMismatch(const String & value,double step) const129 bool NumberInputType::stepMismatch(const String& value, double step) const
130 {
131 double doubleValue;
132 if (!parseToDoubleForNumberType(value, &doubleValue))
133 return false;
134 doubleValue = fabs(doubleValue - stepBase());
135 if (isinf(doubleValue))
136 return false;
137 // double's fractional part size is DBL_MAN_DIG-bit. If the current value
138 // is greater than step*2^DBL_MANT_DIG, the following computation for
139 // remainder makes no sense.
140 if (doubleValue / pow(2.0, DBL_MANT_DIG) > step)
141 return false;
142 // The computation follows HTML5 4.10.7.2.10 `The step attribute' :
143 // ... that number subtracted from the step base is not an integral multiple
144 // of the allowed value step, the element is suffering from a step mismatch.
145 double remainder = fabs(doubleValue - step * round(doubleValue / step));
146 // Accepts erros in lower fractional part which IEEE 754 single-precision
147 // can't represent.
148 double computedAcceptableError = acceptableError(step);
149 return computedAcceptableError < remainder && remainder < (step - computedAcceptableError);
150 }
151
stepBase() const152 double NumberInputType::stepBase() const
153 {
154 return parseToDouble(element()->fastGetAttribute(minAttr), defaultStepBase());
155 }
156
stepBaseWithDecimalPlaces(unsigned * decimalPlaces) const157 double NumberInputType::stepBaseWithDecimalPlaces(unsigned* decimalPlaces) const
158 {
159 return parseToDoubleWithDecimalPlaces(element()->fastGetAttribute(minAttr), defaultStepBase(), decimalPlaces);
160 }
161
defaultStep() const162 double NumberInputType::defaultStep() const
163 {
164 return numberDefaultStep;
165 }
166
stepScaleFactor() const167 double NumberInputType::stepScaleFactor() const
168 {
169 return numberStepScaleFactor;
170 }
171
handleKeydownEvent(KeyboardEvent * event)172 void NumberInputType::handleKeydownEvent(KeyboardEvent* event)
173 {
174 handleKeydownEventForSpinButton(event);
175 if (!event->defaultHandled())
176 TextFieldInputType::handleKeydownEvent(event);
177 }
178
handleWheelEvent(WheelEvent * event)179 void NumberInputType::handleWheelEvent(WheelEvent* event)
180 {
181 handleWheelEventForSpinButton(event);
182 }
183
parseToDouble(const String & src,double defaultValue) const184 double NumberInputType::parseToDouble(const String& src, double defaultValue) const
185 {
186 double numberValue;
187 if (!parseToDoubleForNumberType(src, &numberValue))
188 return defaultValue;
189 ASSERT(isfinite(numberValue));
190 return numberValue;
191 }
192
parseToDoubleWithDecimalPlaces(const String & src,double defaultValue,unsigned * decimalPlaces) const193 double NumberInputType::parseToDoubleWithDecimalPlaces(const String& src, double defaultValue, unsigned *decimalPlaces) const
194 {
195 double numberValue;
196 if (!parseToDoubleForNumberTypeWithDecimalPlaces(src, &numberValue, decimalPlaces))
197 return defaultValue;
198 ASSERT(isfinite(numberValue));
199 return numberValue;
200 }
201
serialize(double value) const202 String NumberInputType::serialize(double value) const
203 {
204 if (!isfinite(value))
205 return String();
206 return serializeForNumberType(value);
207 }
208
acceptableError(double step) const209 double NumberInputType::acceptableError(double step) const
210 {
211 return step / pow(2.0, FLT_MANT_DIG);
212 }
213
handleBlurEvent()214 void NumberInputType::handleBlurEvent()
215 {
216 // Reset the renderer value, which might be unmatched with the element value.
217 element()->setFormControlValueMatchesRenderer(false);
218
219 // We need to reset the renderer value explicitly because an unacceptable
220 // renderer value should be purged before style calculation.
221 if (element()->renderer())
222 element()->renderer()->updateFromElement();
223 }
224
visibleValue() const225 String NumberInputType::visibleValue() const
226 {
227 String currentValue = element()->value();
228 if (currentValue.isEmpty())
229 return currentValue;
230 double doubleValue = numeric_limits<double>::quiet_NaN();
231 unsigned decimalPlace;
232 parseToDoubleForNumberTypeWithDecimalPlaces(currentValue, &doubleValue, &decimalPlace);
233 String localized = formatLocalizedNumber(doubleValue, decimalPlace);
234 return localized.isEmpty() ? currentValue : localized;
235 }
236
convertFromVisibleValue(const String & visibleValue) const237 String NumberInputType::convertFromVisibleValue(const String& visibleValue) const
238 {
239 if (visibleValue.isEmpty())
240 return visibleValue;
241 double parsedNumber = parseLocalizedNumber(visibleValue);
242 return isfinite(parsedNumber) ? serializeForNumberType(parsedNumber) : visibleValue;
243 }
244
isAcceptableValue(const String & proposedValue)245 bool NumberInputType::isAcceptableValue(const String& proposedValue)
246 {
247 return proposedValue.isEmpty() || isfinite(parseLocalizedNumber(proposedValue)) || parseToDoubleForNumberType(proposedValue, 0);
248 }
249
sanitizeValue(const String & proposedValue)250 String NumberInputType::sanitizeValue(const String& proposedValue)
251 {
252 if (proposedValue.isEmpty())
253 return proposedValue;
254 return parseToDoubleForNumberType(proposedValue, 0) ? proposedValue : emptyAtom.string();
255 }
256
hasUnacceptableValue()257 bool NumberInputType::hasUnacceptableValue()
258 {
259 return element()->renderer() && !isAcceptableValue(toRenderTextControl(element()->renderer())->text());
260 }
261
shouldRespectSpeechAttribute()262 bool NumberInputType::shouldRespectSpeechAttribute()
263 {
264 return true;
265 }
266
isNumberField() const267 bool NumberInputType::isNumberField() const
268 {
269 return true;
270 }
271
272 } // namespace WebCore
273