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