Home | History | Annotate | Download | only in html
      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 PassOwnPtr<InputType> NumberInputType::create(HTMLInputElement* element)
     57 {
     58     return adoptPtr(new NumberInputType(element));
     59 }
     60 
     61 const AtomicString& NumberInputType::formControlType() const
     62 {
     63     return InputTypeNames::number();
     64 }
     65 
     66 double NumberInputType::valueAsNumber() const
     67 {
     68     return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN());
     69 }
     70 
     71 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 
     84 bool NumberInputType::typeMismatchFor(const String& value) const
     85 {
     86     return !value.isEmpty() && !parseToDoubleForNumberType(value, 0);
     87 }
     88 
     89 bool NumberInputType::typeMismatch() const
     90 {
     91     ASSERT(!typeMismatchFor(element()->value()));
     92     return false;
     93 }
     94 
     95 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 
    102 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 
    109 bool NumberInputType::supportsRangeLimitation() const
    110 {
    111     return true;
    112 }
    113 
    114 double NumberInputType::minimum() const
    115 {
    116     return parseToDouble(element()->fastGetAttribute(minAttr), -numeric_limits<float>::max());
    117 }
    118 
    119 double NumberInputType::maximum() const
    120 {
    121     return parseToDouble(element()->fastGetAttribute(maxAttr), numeric_limits<float>::max());
    122 }
    123 
    124 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 
    147 double NumberInputType::stepBase() const
    148 {
    149     return parseToDouble(element()->fastGetAttribute(minAttr), defaultStepBase());
    150 }
    151 
    152 double NumberInputType::stepBaseWithDecimalPlaces(unsigned* decimalPlaces) const
    153 {
    154     return parseToDoubleWithDecimalPlaces(element()->fastGetAttribute(minAttr), defaultStepBase(), decimalPlaces);
    155 }
    156 
    157 double NumberInputType::defaultStep() const
    158 {
    159     return numberDefaultStep;
    160 }
    161 
    162 double NumberInputType::stepScaleFactor() const
    163 {
    164     return numberStepScaleFactor;
    165 }
    166 
    167 void NumberInputType::handleKeydownEvent(KeyboardEvent* event)
    168 {
    169     handleKeydownEventForSpinButton(event);
    170     if (!event->defaultHandled())
    171         TextFieldInputType::handleKeydownEvent(event);
    172 }
    173 
    174 void NumberInputType::handleWheelEvent(WheelEvent* event)
    175 {
    176     handleWheelEventForSpinButton(event);
    177 }
    178 
    179 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 
    188 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 
    197 String NumberInputType::serialize(double value) const
    198 {
    199     if (!isfinite(value))
    200         return String();
    201     return serializeForNumberType(value);
    202 }
    203 
    204 double NumberInputType::acceptableError(double step) const
    205 {
    206     return step / pow(2.0, FLT_MANT_DIG);
    207 }
    208 
    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 
    220 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 
    232 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 
    240 bool NumberInputType::isAcceptableValue(const String& proposedValue)
    241 {
    242     return proposedValue.isEmpty() || isfinite(parseLocalizedNumber(proposedValue)) || parseToDoubleForNumberType(proposedValue, 0);
    243 }
    244 
    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 
    252 bool NumberInputType::hasUnacceptableValue()
    253 {
    254     return element()->renderer() && !isAcceptableValue(toRenderTextControl(element()->renderer())->text());
    255 }
    256 
    257 bool NumberInputType::shouldRespectSpeechAttribute()
    258 {
    259     return true;
    260 }
    261 
    262 bool NumberInputType::isNumberField() const
    263 {
    264     return true;
    265 }
    266 
    267 bool NumberInputType::hasSpinButton()
    268 {
    269     return true;
    270 }
    271 
    272 } // namespace WebCore
    273