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
48namespace WebCore {
49
50using namespace HTMLNames;
51using namespace std;
52
53static const double numberDefaultStep = 1.0;
54static const double numberStepScaleFactor = 1.0;
55
56PassOwnPtr<InputType> NumberInputType::create(HTMLInputElement* element)
57{
58    return adoptPtr(new NumberInputType(element));
59}
60
61const AtomicString& NumberInputType::formControlType() const
62{
63    return InputTypeNames::number();
64}
65
66double NumberInputType::valueAsNumber() const
67{
68    return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN());
69}
70
71void 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
84bool NumberInputType::typeMismatchFor(const String& value) const
85{
86    return !value.isEmpty() && !parseToDoubleForNumberType(value, 0);
87}
88
89bool NumberInputType::typeMismatch() const
90{
91    ASSERT(!typeMismatchFor(element()->value()));
92    return false;
93}
94
95bool 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
102bool 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
109bool NumberInputType::supportsRangeLimitation() const
110{
111    return true;
112}
113
114double NumberInputType::minimum() const
115{
116    return parseToDouble(element()->fastGetAttribute(minAttr), -numeric_limits<float>::max());
117}
118
119double NumberInputType::maximum() const
120{
121    return parseToDouble(element()->fastGetAttribute(maxAttr), numeric_limits<float>::max());
122}
123
124bool NumberInputType::stepMismatch(const String& value, double step) const
125{
126    double doubleValue;
127    if (!parseToDoubleForNumberType(value, &doubleValue))
128        return false;
129    doubleValue = fabs(doubleValue - stepBase());
130    if (isinf(doubleValue))
131        return false;
132    // double's fractional part size is DBL_MAN_DIG-bit. If the current value
133    // is greater than step*2^DBL_MANT_DIG, the following computation for
134    // remainder makes no sense.
135    if (doubleValue / pow(2.0, DBL_MANT_DIG) > step)
136        return false;
137    // The computation follows HTML5 4.10.7.2.10 `The step attribute' :
138    // ... that number subtracted from the step base is not an integral multiple
139    // of the allowed value step, the element is suffering from a step mismatch.
140    double remainder = fabs(doubleValue - step * round(doubleValue / step));
141    // Accepts erros in lower fractional part which IEEE 754 single-precision
142    // can't represent.
143    double computedAcceptableError = acceptableError(step);
144    return computedAcceptableError < remainder && remainder < (step - computedAcceptableError);
145}
146
147double NumberInputType::stepBase() const
148{
149    return parseToDouble(element()->fastGetAttribute(minAttr), defaultStepBase());
150}
151
152double NumberInputType::stepBaseWithDecimalPlaces(unsigned* decimalPlaces) const
153{
154    return parseToDoubleWithDecimalPlaces(element()->fastGetAttribute(minAttr), defaultStepBase(), decimalPlaces);
155}
156
157double NumberInputType::defaultStep() const
158{
159    return numberDefaultStep;
160}
161
162double NumberInputType::stepScaleFactor() const
163{
164    return numberStepScaleFactor;
165}
166
167void NumberInputType::handleKeydownEvent(KeyboardEvent* event)
168{
169    handleKeydownEventForSpinButton(event);
170    if (!event->defaultHandled())
171        TextFieldInputType::handleKeydownEvent(event);
172}
173
174void NumberInputType::handleWheelEvent(WheelEvent* event)
175{
176    handleWheelEventForSpinButton(event);
177}
178
179double NumberInputType::parseToDouble(const String& src, double defaultValue) const
180{
181    double numberValue;
182    if (!parseToDoubleForNumberType(src, &numberValue))
183        return defaultValue;
184    ASSERT(isfinite(numberValue));
185    return numberValue;
186}
187
188double NumberInputType::parseToDoubleWithDecimalPlaces(const String& src, double defaultValue, unsigned *decimalPlaces) const
189{
190    double numberValue;
191    if (!parseToDoubleForNumberTypeWithDecimalPlaces(src, &numberValue, decimalPlaces))
192        return defaultValue;
193    ASSERT(isfinite(numberValue));
194    return numberValue;
195}
196
197String NumberInputType::serialize(double value) const
198{
199    if (!isfinite(value))
200        return String();
201    return serializeForNumberType(value);
202}
203
204double NumberInputType::acceptableError(double step) const
205{
206    return step / pow(2.0, FLT_MANT_DIG);
207}
208
209void NumberInputType::handleBlurEvent()
210{
211    // Reset the renderer value, which might be unmatched with the element value.
212    element()->setFormControlValueMatchesRenderer(false);
213
214    // We need to reset the renderer value explicitly because an unacceptable
215    // renderer value should be purged before style calculation.
216    if (element()->renderer())
217        element()->renderer()->updateFromElement();
218}
219
220String NumberInputType::visibleValue() const
221{
222    String currentValue = element()->value();
223    if (currentValue.isEmpty())
224        return currentValue;
225    double doubleValue = numeric_limits<double>::quiet_NaN();
226    unsigned decimalPlace;
227    parseToDoubleForNumberTypeWithDecimalPlaces(currentValue, &doubleValue, &decimalPlace);
228    String localized = formatLocalizedNumber(doubleValue, decimalPlace);
229    return localized.isEmpty() ? currentValue : localized;
230}
231
232String NumberInputType::convertFromVisibleValue(const String& visibleValue) const
233{
234    if (visibleValue.isEmpty())
235        return visibleValue;
236    double parsedNumber = parseLocalizedNumber(visibleValue);
237    return isfinite(parsedNumber) ? serializeForNumberType(parsedNumber) : visibleValue;
238}
239
240bool NumberInputType::isAcceptableValue(const String& proposedValue)
241{
242    return proposedValue.isEmpty() || isfinite(parseLocalizedNumber(proposedValue)) || parseToDoubleForNumberType(proposedValue, 0);
243}
244
245String NumberInputType::sanitizeValue(const String& proposedValue)
246{
247    if (proposedValue.isEmpty())
248        return proposedValue;
249    return parseToDoubleForNumberType(proposedValue, 0) ? proposedValue : emptyAtom.string();
250}
251
252bool NumberInputType::hasUnacceptableValue()
253{
254    return element()->renderer() && !isAcceptableValue(toRenderTextControl(element()->renderer())->text());
255}
256
257bool NumberInputType::shouldRespectSpeechAttribute()
258{
259    return true;
260}
261
262bool NumberInputType::isNumberField() const
263{
264    return true;
265}
266
267bool NumberInputType::hasSpinButton()
268{
269    return true;
270}
271
272} // namespace WebCore
273