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