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 "core/html/forms/NumberInputType.h"
34
35#include "bindings/core/v8/ExceptionState.h"
36#include "core/HTMLNames.h"
37#include "core/InputTypeNames.h"
38#include "core/dom/ExceptionCode.h"
39#include "core/events/KeyboardEvent.h"
40#include "core/html/HTMLInputElement.h"
41#include "core/html/parser/HTMLParserIdioms.h"
42#include "core/rendering/RenderTextControl.h"
43#include "platform/text/PlatformLocale.h"
44#include "wtf/MathExtras.h"
45#include "wtf/PassOwnPtr.h"
46#include <limits>
47
48namespace blink {
49
50using blink::WebLocalizedString;
51using namespace HTMLNames;
52
53static const int numberDefaultStep = 1;
54static const int numberDefaultStepBase = 0;
55static const int numberStepScaleFactor = 1;
56
57struct RealNumberRenderSize {
58    unsigned sizeBeforeDecimalPoint;
59    unsigned sizeAfteDecimalPoint;
60
61    RealNumberRenderSize(unsigned before, unsigned after)
62        : sizeBeforeDecimalPoint(before)
63        , sizeAfteDecimalPoint(after)
64    {
65    }
66
67    RealNumberRenderSize max(const RealNumberRenderSize& other) const
68    {
69        return RealNumberRenderSize(
70            std::max(sizeBeforeDecimalPoint, other.sizeBeforeDecimalPoint),
71            std::max(sizeAfteDecimalPoint, other.sizeAfteDecimalPoint));
72    }
73};
74
75static RealNumberRenderSize calculateRenderSize(const Decimal& value)
76{
77    ASSERT(value.isFinite());
78    const unsigned sizeOfDigits = String::number(value.value().coefficient()).length();
79    const unsigned sizeOfSign = value.isNegative() ? 1 : 0;
80    const int exponent = value.exponent();
81    if (exponent >= 0)
82        return RealNumberRenderSize(sizeOfSign + sizeOfDigits, 0);
83
84    const int sizeBeforeDecimalPoint = exponent + sizeOfDigits;
85    if (sizeBeforeDecimalPoint > 0) {
86        // In case of "123.456"
87        return RealNumberRenderSize(sizeOfSign + sizeBeforeDecimalPoint, sizeOfDigits - sizeBeforeDecimalPoint);
88    }
89
90    // In case of "0.00012345"
91    const unsigned sizeOfZero = 1;
92    const unsigned numberOfZeroAfterDecimalPoint = -sizeBeforeDecimalPoint;
93    return RealNumberRenderSize(sizeOfSign + sizeOfZero , numberOfZeroAfterDecimalPoint + sizeOfDigits);
94}
95
96PassRefPtrWillBeRawPtr<InputType> NumberInputType::create(HTMLInputElement& element)
97{
98    return adoptRefWillBeNoop(new NumberInputType(element));
99}
100
101void NumberInputType::countUsage()
102{
103    countUsageIfVisible(UseCounter::InputTypeNumber);
104}
105
106const AtomicString& NumberInputType::formControlType() const
107{
108    return InputTypeNames::number;
109}
110
111void NumberInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
112{
113    if (!valueChanged && sanitizedValue.isEmpty() && !element().innerEditorValue().isEmpty())
114        element().updateView();
115    TextFieldInputType::setValue(sanitizedValue, valueChanged, eventBehavior);
116}
117
118double NumberInputType::valueAsDouble() const
119{
120    return parseToDoubleForNumberType(element().value());
121}
122
123void NumberInputType::setValueAsDouble(double newValue, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState) const
124{
125    element().setValue(serializeForNumberType(newValue), eventBehavior);
126}
127
128void NumberInputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState) const
129{
130    element().setValue(serializeForNumberType(newValue), eventBehavior);
131}
132
133bool NumberInputType::typeMismatchFor(const String& value) const
134{
135    return !value.isEmpty() && !std::isfinite(parseToDoubleForNumberType(value));
136}
137
138bool NumberInputType::typeMismatch() const
139{
140    ASSERT(!typeMismatchFor(element().value()));
141    return false;
142}
143
144StepRange NumberInputType::createStepRange(AnyStepHandling anyStepHandling) const
145{
146    DEFINE_STATIC_LOCAL(const StepRange::StepDescription, stepDescription, (numberDefaultStep, numberDefaultStepBase, numberStepScaleFactor));
147    const Decimal doubleMax = Decimal::fromDouble(std::numeric_limits<double>::max());
148    return InputType::createStepRange(anyStepHandling, numberDefaultStepBase, -doubleMax, doubleMax, stepDescription);
149}
150
151bool NumberInputType::sizeShouldIncludeDecoration(int defaultSize, int& preferredSize) const
152{
153    preferredSize = defaultSize;
154
155    const String stepString = element().fastGetAttribute(stepAttr);
156    if (equalIgnoringCase(stepString, "any"))
157        return false;
158
159    const Decimal minimum = parseToDecimalForNumberType(element().fastGetAttribute(minAttr));
160    if (!minimum.isFinite())
161        return false;
162
163    const Decimal maximum = parseToDecimalForNumberType(element().fastGetAttribute(maxAttr));
164    if (!maximum.isFinite())
165        return false;
166
167    const Decimal step = parseToDecimalForNumberType(stepString, 1);
168    ASSERT(step.isFinite());
169
170    RealNumberRenderSize size = calculateRenderSize(minimum).max(calculateRenderSize(maximum).max(calculateRenderSize(step)));
171
172    preferredSize = size.sizeBeforeDecimalPoint + size.sizeAfteDecimalPoint + (size.sizeAfteDecimalPoint ? 1 : 0);
173
174    return true;
175}
176
177bool NumberInputType::isSteppable() const
178{
179    return true;
180}
181
182void NumberInputType::handleKeydownEvent(KeyboardEvent* event)
183{
184    handleKeydownEventForSpinButton(event);
185    if (!event->defaultHandled())
186        TextFieldInputType::handleKeydownEvent(event);
187}
188
189Decimal NumberInputType::parseToNumber(const String& src, const Decimal& defaultValue) const
190{
191    return parseToDecimalForNumberType(src, defaultValue);
192}
193
194String NumberInputType::serialize(const Decimal& value) const
195{
196    if (!value.isFinite())
197        return String();
198    return serializeForNumberType(value);
199}
200
201static bool isE(UChar ch)
202{
203    return ch == 'e' || ch == 'E';
204}
205
206String NumberInputType::localizeValue(const String& proposedValue) const
207{
208    if (proposedValue.isEmpty())
209        return proposedValue;
210    // We don't localize scientific notations.
211    if (proposedValue.find(isE) != kNotFound)
212        return proposedValue;
213    return element().locale().convertToLocalizedNumber(proposedValue);
214}
215
216String NumberInputType::visibleValue() const
217{
218    return localizeValue(element().value());
219}
220
221String NumberInputType::convertFromVisibleValue(const String& visibleValue) const
222{
223    if (visibleValue.isEmpty())
224        return visibleValue;
225    // We don't localize scientific notations.
226    if (visibleValue.find(isE) != kNotFound)
227        return visibleValue;
228    return element().locale().convertFromLocalizedNumber(visibleValue);
229}
230
231String NumberInputType::sanitizeValue(const String& proposedValue) const
232{
233    if (proposedValue.isEmpty())
234        return proposedValue;
235    return std::isfinite(parseToDoubleForNumberType(proposedValue)) ? proposedValue : emptyString();
236}
237
238bool NumberInputType::hasBadInput() const
239{
240    String standardValue = convertFromVisibleValue(element().innerEditorValue());
241    return !standardValue.isEmpty() && !std::isfinite(parseToDoubleForNumberType(standardValue));
242}
243
244String NumberInputType::badInputText() const
245{
246    return locale().queryString(WebLocalizedString::ValidationBadInputForNumber);
247}
248
249String NumberInputType::rangeOverflowText(const Decimal& maximum) const
250{
251    return locale().queryString(WebLocalizedString::ValidationRangeOverflow, localizeValue(serialize(maximum)));
252}
253
254String NumberInputType::rangeUnderflowText(const Decimal& minimum) const
255{
256    return locale().queryString(WebLocalizedString::ValidationRangeUnderflow, localizeValue(serialize(minimum)));
257}
258
259bool NumberInputType::shouldRespectSpeechAttribute()
260{
261    return true;
262}
263
264bool NumberInputType::supportsPlaceholder() const
265{
266    return true;
267}
268
269void NumberInputType::minOrMaxAttributeChanged()
270{
271    InputType::minOrMaxAttributeChanged();
272
273    if (element().renderer())
274        element().renderer()->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
275}
276
277void NumberInputType::stepAttributeChanged()
278{
279    InputType::stepAttributeChanged();
280
281    if (element().renderer())
282        element().renderer()->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
283}
284
285bool NumberInputType::supportsSelectionAPI() const
286{
287    return false;
288}
289
290} // namespace blink
291