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