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