Do not localize numbers in scientific notation
[WebKit-https.git] / Source / WebCore / html / NumberInputType.cpp
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
56 static unsigned lengthBeforeDecimalPoint(double value)
57 {
58     // If value is negative, '-' should be counted.
59
60     double absoluteValue = fabs(value);
61     if (absoluteValue < 1)
62         return value < 0 ? 2 : 1;
63
64     unsigned length = static_cast<unsigned>(log10(floor(absoluteValue))) + 1;
65     if (value < 0)
66         length += 1;
67
68     return length;
69 }
70
71 PassOwnPtr<InputType> NumberInputType::create(HTMLInputElement* element)
72 {
73     return adoptPtr(new NumberInputType(element));
74 }
75
76 const AtomicString& NumberInputType::formControlType() const
77 {
78     return InputTypeNames::number();
79 }
80
81 double NumberInputType::valueAsNumber() const
82 {
83     return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN());
84 }
85
86 void NumberInputType::setValueAsNumber(double newValue, bool sendChangeEvent, ExceptionCode& ec) const
87 {
88     if (newValue < -numeric_limits<float>::max()) {
89         ec = INVALID_STATE_ERR;
90         return;
91     }
92     if (newValue > numeric_limits<float>::max()) {
93         ec = INVALID_STATE_ERR;
94         return;
95     }
96     element()->setValue(serialize(newValue), sendChangeEvent);
97 }
98
99 bool NumberInputType::typeMismatchFor(const String& value) const
100 {
101     return !value.isEmpty() && !parseToDoubleForNumberType(value, 0);
102 }
103
104 bool NumberInputType::typeMismatch() const
105 {
106     ASSERT(!typeMismatchFor(element()->value()));
107     return false;
108 }
109
110 bool NumberInputType::rangeUnderflow(const String& value) const
111 {
112     const double nan = numeric_limits<double>::quiet_NaN();
113     double doubleValue = parseToDouble(value, nan);
114     return isfinite(doubleValue) && doubleValue < minimum();
115 }
116
117 bool NumberInputType::rangeOverflow(const String& value) const
118 {
119     const double nan = numeric_limits<double>::quiet_NaN();
120     double doubleValue = parseToDouble(value, nan);
121     return isfinite(doubleValue) && doubleValue > maximum();
122 }
123
124 bool NumberInputType::supportsRangeLimitation() const
125 {
126     return true;
127 }
128
129 double NumberInputType::minimum() const
130 {
131     return parseToDouble(element()->fastGetAttribute(minAttr), -numeric_limits<float>::max());
132 }
133
134 double NumberInputType::maximum() const
135 {
136     return parseToDouble(element()->fastGetAttribute(maxAttr), numeric_limits<float>::max());
137 }
138
139 bool NumberInputType::sizeShouldIncludeDecoration(int defaultSize, int& preferredSize) const
140 {
141     preferredSize = defaultSize;
142
143     unsigned minValueDecimalPlaces;
144     double minValueDouble;
145     String minValue = element()->fastGetAttribute(minAttr);
146     if (!parseToDoubleForNumberTypeWithDecimalPlaces(minValue, &minValueDouble, &minValueDecimalPlaces))
147         return false;
148
149     unsigned maxValueDecimalPlaces;
150     double maxValueDouble;
151     String maxValue = element()->fastGetAttribute(maxAttr);
152     if (!parseToDoubleForNumberTypeWithDecimalPlaces(maxValue, &maxValueDouble, &maxValueDecimalPlaces))
153         return false;
154
155     if (maxValueDouble < minValueDouble) {
156         maxValueDouble = minValueDouble;
157         maxValueDecimalPlaces = minValueDecimalPlaces;
158     }
159
160     unsigned stepValueDecimalPlaces;
161     double stepValueDouble;
162     String stepValue = element()->fastGetAttribute(stepAttr);
163     if (equalIgnoringCase(stepValue, "any"))
164         return false;
165     if (!parseToDoubleForNumberTypeWithDecimalPlaces(stepValue, &stepValueDouble, &stepValueDecimalPlaces)) {
166         stepValueDouble = 1;
167         stepValueDecimalPlaces = 0;
168     }
169
170     unsigned length = lengthBeforeDecimalPoint(minValueDouble);
171     length = max(length, lengthBeforeDecimalPoint(maxValueDouble));
172     length = max(length, lengthBeforeDecimalPoint(stepValueDouble));
173
174     unsigned lengthAfterDecimalPoint = minValueDecimalPlaces;
175     lengthAfterDecimalPoint = max(lengthAfterDecimalPoint, maxValueDecimalPlaces);
176     lengthAfterDecimalPoint = max(lengthAfterDecimalPoint, stepValueDecimalPlaces);
177
178     // '.' should be counted if the value has decimal places.
179     if (lengthAfterDecimalPoint > 0)
180         length += lengthAfterDecimalPoint + 1;
181
182     preferredSize = length;
183     return true;
184 }
185
186 bool NumberInputType::isSteppable() const
187 {
188     return true;
189 }
190
191 bool NumberInputType::stepMismatch(const String& value, double step) const
192 {
193     double doubleValue;
194     if (!parseToDoubleForNumberType(value, &doubleValue))
195         return false;
196     doubleValue = fabs(doubleValue - stepBase());
197     if (isinf(doubleValue))
198         return false;
199     // double's fractional part size is DBL_MAN_DIG-bit. If the current value
200     // is greater than step*2^DBL_MANT_DIG, the following computation for
201     // remainder makes no sense.
202     if (doubleValue / pow(2.0, DBL_MANT_DIG) > step)
203         return false;
204     // The computation follows HTML5 4.10.7.2.10 `The step attribute' :
205     // ... that number subtracted from the step base is not an integral multiple
206     // of the allowed value step, the element is suffering from a step mismatch.
207     double remainder = fabs(doubleValue - step * round(doubleValue / step));
208     // Accepts erros in lower fractional part which IEEE 754 single-precision
209     // can't represent.
210     double computedAcceptableError = acceptableError(step);
211     return computedAcceptableError < remainder && remainder < (step - computedAcceptableError);
212 }
213
214 double NumberInputType::stepBase() const
215 {
216     return parseToDouble(element()->fastGetAttribute(minAttr), defaultStepBase());
217 }
218
219 double NumberInputType::stepBaseWithDecimalPlaces(unsigned* decimalPlaces) const
220 {
221     return parseToDoubleWithDecimalPlaces(element()->fastGetAttribute(minAttr), defaultStepBase(), decimalPlaces);
222 }
223
224 double NumberInputType::defaultStep() const
225 {
226     return numberDefaultStep;
227 }
228
229 double NumberInputType::stepScaleFactor() const
230 {
231     return numberStepScaleFactor;
232 }
233
234 void NumberInputType::handleKeydownEvent(KeyboardEvent* event)
235 {
236     handleKeydownEventForSpinButton(event);
237     if (!event->defaultHandled())
238         TextFieldInputType::handleKeydownEvent(event);
239 }
240
241 void NumberInputType::handleWheelEvent(WheelEvent* event)
242 {
243     handleWheelEventForSpinButton(event);
244 }
245
246 double NumberInputType::parseToDouble(const String& src, double defaultValue) const
247 {
248     double numberValue;
249     if (!parseToDoubleForNumberType(src, &numberValue))
250         return defaultValue;
251     ASSERT(isfinite(numberValue));
252     return numberValue;
253 }
254
255 double NumberInputType::parseToDoubleWithDecimalPlaces(const String& src, double defaultValue, unsigned *decimalPlaces) const
256 {
257     double numberValue;
258     if (!parseToDoubleForNumberTypeWithDecimalPlaces(src, &numberValue, decimalPlaces))
259         return defaultValue;
260     ASSERT(isfinite(numberValue));
261     return numberValue;
262 }
263
264 String NumberInputType::serialize(double value) const
265 {
266     if (!isfinite(value))
267         return String();
268     return serializeForNumberType(value);
269 }
270
271 double NumberInputType::acceptableError(double step) const
272 {
273     return step / pow(2.0, FLT_MANT_DIG);
274 }
275
276 void NumberInputType::handleBlurEvent()
277 {
278     // Reset the renderer value, which might be unmatched with the element value.
279     element()->setFormControlValueMatchesRenderer(false);
280
281     // We need to reset the renderer value explicitly because an unacceptable
282     // renderer value should be purged before style calculation.
283     element()->updateInnerTextValue();
284 }
285
286 static bool isE(UChar ch)
287 {
288     return ch == 'e' || ch == 'E';
289 }
290
291 String NumberInputType::visibleValue() const
292 {
293     String currentValue = element()->value();
294     if (currentValue.isEmpty())
295         return currentValue;
296     // We don't localize scientific notations.
297     if (currentValue.find(isE) != notFound)
298         return currentValue;
299     // FIXME: The following three lines should be removed when we
300     // remove the second argument of convertToLocalizedNumber().
301     double doubleValue = numeric_limits<double>::quiet_NaN();
302     unsigned decimalPlace;
303     parseToDoubleForNumberTypeWithDecimalPlaces(currentValue, &doubleValue, &decimalPlace);
304     return convertToLocalizedNumber(currentValue, decimalPlace);
305 }
306
307 String NumberInputType::convertFromVisibleValue(const String& visibleValue) const
308 {
309     if (visibleValue.isEmpty())
310         return visibleValue;
311     // We don't localize scientific notations.
312     if (visibleValue.find(isE) != notFound)
313         return visibleValue;
314     return convertFromLocalizedNumber(visibleValue);
315 }
316
317 bool NumberInputType::isAcceptableValue(const String& proposedValue)
318 {
319     String standardValue = convertFromVisibleValue(proposedValue);
320     return standardValue.isEmpty() || parseToDoubleForNumberType(standardValue, 0);
321 }
322
323 String NumberInputType::sanitizeValue(const String& proposedValue) const
324 {
325     if (proposedValue.isEmpty())
326         return proposedValue;
327     return parseToDoubleForNumberType(proposedValue, 0) ? proposedValue : emptyAtom.string();
328 }
329
330 bool NumberInputType::hasUnacceptableValue()
331 {
332     return element()->renderer() && !isAcceptableValue(element()->innerTextValue());
333 }
334
335 bool NumberInputType::shouldRespectSpeechAttribute()
336 {
337     return true;
338 }
339
340 bool NumberInputType::supportsPlaceholder() const
341 {
342     return true;
343 }
344
345 bool NumberInputType::isNumberField() const
346 {
347     return true;
348 }
349
350 void NumberInputType::minOrMaxAttributeChanged()
351 {
352     InputType::minOrMaxAttributeChanged();
353
354     if (element()->renderer())
355         element()->renderer()->setNeedsLayoutAndPrefWidthsRecalc();
356 }
357
358 void NumberInputType::stepAttributeChanged()
359 {
360     InputType::stepAttributeChanged();
361
362     if (element()->renderer())
363         element()->renderer()->setNeedsLayoutAndPrefWidthsRecalc();
364 }
365
366 } // namespace WebCore