• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
stepMismatch(const String & value,double step) const124 bool 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 
stepBase() const147 double NumberInputType::stepBase() const
148 {
149     return parseToDouble(element()->fastGetAttribute(minAttr), defaultStepBase());
150 }
151 
stepBaseWithDecimalPlaces(unsigned * decimalPlaces) const152 double NumberInputType::stepBaseWithDecimalPlaces(unsigned* decimalPlaces) const
153 {
154     return parseToDoubleWithDecimalPlaces(element()->fastGetAttribute(minAttr), defaultStepBase(), decimalPlaces);
155 }
156 
defaultStep() const157 double NumberInputType::defaultStep() const
158 {
159     return numberDefaultStep;
160 }
161 
stepScaleFactor() const162 double NumberInputType::stepScaleFactor() const
163 {
164     return numberStepScaleFactor;
165 }
166 
handleKeydownEvent(KeyboardEvent * event)167 void NumberInputType::handleKeydownEvent(KeyboardEvent* event)
168 {
169     handleKeydownEventForSpinButton(event);
170     if (!event->defaultHandled())
171         TextFieldInputType::handleKeydownEvent(event);
172 }
173 
handleWheelEvent(WheelEvent * event)174 void NumberInputType::handleWheelEvent(WheelEvent* event)
175 {
176     handleWheelEventForSpinButton(event);
177 }
178 
parseToDouble(const String & src,double defaultValue) const179 double 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 
parseToDoubleWithDecimalPlaces(const String & src,double defaultValue,unsigned * decimalPlaces) const188 double 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 
serialize(double value) const197 String NumberInputType::serialize(double value) const
198 {
199     if (!isfinite(value))
200         return String();
201     return serializeForNumberType(value);
202 }
203 
acceptableError(double step) const204 double NumberInputType::acceptableError(double step) const
205 {
206     return step / pow(2.0, FLT_MANT_DIG);
207 }
208 
handleBlurEvent()209 void 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 
visibleValue() const220 String 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 
convertFromVisibleValue(const String & visibleValue) const232 String 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 
isAcceptableValue(const String & proposedValue)240 bool NumberInputType::isAcceptableValue(const String& proposedValue)
241 {
242     return proposedValue.isEmpty() || isfinite(parseLocalizedNumber(proposedValue)) || parseToDoubleForNumberType(proposedValue, 0);
243 }
244 
sanitizeValue(const String & proposedValue)245 String NumberInputType::sanitizeValue(const String& proposedValue)
246 {
247     if (proposedValue.isEmpty())
248         return proposedValue;
249     return parseToDoubleForNumberType(proposedValue, 0) ? proposedValue : emptyAtom.string();
250 }
251 
hasUnacceptableValue()252 bool NumberInputType::hasUnacceptableValue()
253 {
254     return element()->renderer() && !isAcceptableValue(toRenderTextControl(element()->renderer())->text());
255 }
256 
shouldRespectSpeechAttribute()257 bool NumberInputType::shouldRespectSpeechAttribute()
258 {
259     return true;
260 }
261 
isNumberField() const262 bool NumberInputType::isNumberField() const
263 {
264     return true;
265 }
266 
hasSpinButton()267 bool NumberInputType::hasSpinButton()
268 {
269     return true;
270 }
271 
272 } // namespace WebCore
273