Home | History | Annotate | Download | only in forms
      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/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 
     48 namespace WebCore {
     49 
     50 using blink::WebLocalizedString;
     51 using namespace HTMLNames;
     52 
     53 static const int numberDefaultStep = 1;
     54 static const int numberDefaultStepBase = 0;
     55 static const int numberStepScaleFactor = 1;
     56 
     57 struct 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 
     75 static 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 
     96 PassRefPtrWillBeRawPtr<InputType> NumberInputType::create(HTMLInputElement& element)
     97 {
     98     return adoptRefWillBeNoop(new NumberInputType(element));
     99 }
    100 
    101 void NumberInputType::countUsage()
    102 {
    103     countUsageIfVisible(UseCounter::InputTypeNumber);
    104 }
    105 
    106 const AtomicString& NumberInputType::formControlType() const
    107 {
    108     return InputTypeNames::number;
    109 }
    110 
    111 void 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 
    118 double NumberInputType::valueAsDouble() const
    119 {
    120     return parseToDoubleForNumberType(element().value());
    121 }
    122 
    123 void NumberInputType::setValueAsDouble(double newValue, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState) const
    124 {
    125     element().setValue(serializeForNumberType(newValue), eventBehavior);
    126 }
    127 
    128 void NumberInputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState) const
    129 {
    130     element().setValue(serializeForNumberType(newValue), eventBehavior);
    131 }
    132 
    133 bool NumberInputType::typeMismatchFor(const String& value) const
    134 {
    135     return !value.isEmpty() && !std::isfinite(parseToDoubleForNumberType(value));
    136 }
    137 
    138 bool NumberInputType::typeMismatch() const
    139 {
    140     ASSERT(!typeMismatchFor(element().value()));
    141     return false;
    142 }
    143 
    144 StepRange 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 
    151 bool 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 
    177 bool NumberInputType::isSteppable() const
    178 {
    179     return true;
    180 }
    181 
    182 void NumberInputType::handleKeydownEvent(KeyboardEvent* event)
    183 {
    184     handleKeydownEventForSpinButton(event);
    185     if (!event->defaultHandled())
    186         TextFieldInputType::handleKeydownEvent(event);
    187 }
    188 
    189 Decimal NumberInputType::parseToNumber(const String& src, const Decimal& defaultValue) const
    190 {
    191     return parseToDecimalForNumberType(src, defaultValue);
    192 }
    193 
    194 String NumberInputType::serialize(const Decimal& value) const
    195 {
    196     if (!value.isFinite())
    197         return String();
    198     return serializeForNumberType(value);
    199 }
    200 
    201 static bool isE(UChar ch)
    202 {
    203     return ch == 'e' || ch == 'E';
    204 }
    205 
    206 String 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 
    216 String NumberInputType::visibleValue() const
    217 {
    218     return localizeValue(element().value());
    219 }
    220 
    221 String 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 
    231 String NumberInputType::sanitizeValue(const String& proposedValue) const
    232 {
    233     if (proposedValue.isEmpty())
    234         return proposedValue;
    235     return std::isfinite(parseToDoubleForNumberType(proposedValue)) ? proposedValue : emptyString();
    236 }
    237 
    238 bool NumberInputType::hasBadInput() const
    239 {
    240     String standardValue = convertFromVisibleValue(element().innerEditorValue());
    241     return !standardValue.isEmpty() && !std::isfinite(parseToDoubleForNumberType(standardValue));
    242 }
    243 
    244 String NumberInputType::badInputText() const
    245 {
    246     return locale().queryString(WebLocalizedString::ValidationBadInputForNumber);
    247 }
    248 
    249 String NumberInputType::rangeOverflowText(const Decimal& maximum) const
    250 {
    251     return locale().queryString(WebLocalizedString::ValidationRangeOverflow, localizeValue(serialize(maximum)));
    252 }
    253 
    254 String NumberInputType::rangeUnderflowText(const Decimal& minimum) const
    255 {
    256     return locale().queryString(WebLocalizedString::ValidationRangeUnderflow, localizeValue(serialize(minimum)));
    257 }
    258 
    259 bool NumberInputType::shouldRespectSpeechAttribute()
    260 {
    261     return true;
    262 }
    263 
    264 bool NumberInputType::supportsPlaceholder() const
    265 {
    266     return true;
    267 }
    268 
    269 bool NumberInputType::isNumberField() const
    270 {
    271     return true;
    272 }
    273 
    274 void NumberInputType::minOrMaxAttributeChanged()
    275 {
    276     InputType::minOrMaxAttributeChanged();
    277 
    278     if (element().renderer())
    279         element().renderer()->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
    280 }
    281 
    282 void NumberInputType::stepAttributeChanged()
    283 {
    284     InputType::stepAttributeChanged();
    285 
    286     if (element().renderer())
    287         element().renderer()->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
    288 }
    289 
    290 bool NumberInputType::supportsSelectionAPI() const
    291 {
    292     return false;
    293 }
    294 
    295 } // namespace WebCore
    296