NumberInputType.cpp [plain text]
#include "config.h"
#include "NumberInputType.h"
#include "BeforeTextInsertedEvent.h"
#include "ExceptionCode.h"
#include "HTMLInputElement.h"
#include "HTMLNames.h"
#include "HTMLParserIdioms.h"
#include "KeyboardEvent.h"
#include "LocalizedNumber.h"
#include "RenderTextControl.h"
#include <limits>
#include <wtf/ASCIICType.h>
#include <wtf/MathExtras.h>
#include <wtf/PassOwnPtr.h>
namespace WebCore {
using namespace HTMLNames;
using namespace std;
static const double numberDefaultStep = 1.0;
static const double numberStepScaleFactor = 1.0;
static unsigned lengthBeforeDecimalPoint(double value)
{
double absoluteValue = fabs(value);
if (absoluteValue < 1)
return value < 0 ? 2 : 1;
unsigned length = static_cast<unsigned>(log10(floor(absoluteValue))) + 1;
if (value < 0)
length += 1;
return length;
}
PassOwnPtr<InputType> NumberInputType::create(HTMLInputElement* element)
{
return adoptPtr(new NumberInputType(element));
}
const AtomicString& NumberInputType::formControlType() const
{
return InputTypeNames::number();
}
double NumberInputType::valueAsNumber() const
{
return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN());
}
void NumberInputType::setValueAsNumber(double newValue, TextFieldEventBehavior eventBehavior, ExceptionCode& ec) const
{
if (newValue < -numeric_limits<float>::max()) {
ec = INVALID_STATE_ERR;
return;
}
if (newValue > numeric_limits<float>::max()) {
ec = INVALID_STATE_ERR;
return;
}
element()->setValue(serialize(newValue), eventBehavior);
}
bool NumberInputType::typeMismatchFor(const String& value) const
{
return !value.isEmpty() && !parseToDoubleForNumberType(value, 0);
}
bool NumberInputType::typeMismatch() const
{
ASSERT(!typeMismatchFor(element()->value()));
return false;
}
bool NumberInputType::rangeUnderflow(const String& value) const
{
const double nan = numeric_limits<double>::quiet_NaN();
double doubleValue = parseToDouble(value, nan);
return isfinite(doubleValue) && doubleValue < minimum();
}
bool NumberInputType::rangeOverflow(const String& value) const
{
const double nan = numeric_limits<double>::quiet_NaN();
double doubleValue = parseToDouble(value, nan);
return isfinite(doubleValue) && doubleValue > maximum();
}
bool NumberInputType::supportsRangeLimitation() const
{
return true;
}
double NumberInputType::minimum() const
{
return parseToDouble(element()->fastGetAttribute(minAttr), -numeric_limits<float>::max());
}
double NumberInputType::maximum() const
{
return parseToDouble(element()->fastGetAttribute(maxAttr), numeric_limits<float>::max());
}
bool NumberInputType::sizeShouldIncludeDecoration(int defaultSize, int& preferredSize) const
{
preferredSize = defaultSize;
unsigned minValueDecimalPlaces;
double minValueDouble;
String minValue = element()->fastGetAttribute(minAttr);
if (!parseToDoubleForNumberTypeWithDecimalPlaces(minValue, &minValueDouble, &minValueDecimalPlaces))
return false;
unsigned maxValueDecimalPlaces;
double maxValueDouble;
String maxValue = element()->fastGetAttribute(maxAttr);
if (!parseToDoubleForNumberTypeWithDecimalPlaces(maxValue, &maxValueDouble, &maxValueDecimalPlaces))
return false;
if (maxValueDouble < minValueDouble) {
maxValueDouble = minValueDouble;
maxValueDecimalPlaces = minValueDecimalPlaces;
}
unsigned stepValueDecimalPlaces;
double stepValueDouble;
String stepValue = element()->fastGetAttribute(stepAttr);
if (equalIgnoringCase(stepValue, "any"))
return false;
if (!parseToDoubleForNumberTypeWithDecimalPlaces(stepValue, &stepValueDouble, &stepValueDecimalPlaces)) {
stepValueDouble = 1;
stepValueDecimalPlaces = 0;
}
unsigned length = lengthBeforeDecimalPoint(minValueDouble);
length = max(length, lengthBeforeDecimalPoint(maxValueDouble));
length = max(length, lengthBeforeDecimalPoint(stepValueDouble));
unsigned lengthAfterDecimalPoint = minValueDecimalPlaces;
lengthAfterDecimalPoint = max(lengthAfterDecimalPoint, maxValueDecimalPlaces);
lengthAfterDecimalPoint = max(lengthAfterDecimalPoint, stepValueDecimalPlaces);
if (lengthAfterDecimalPoint > 0)
length += lengthAfterDecimalPoint + 1;
preferredSize = length;
return true;
}
bool NumberInputType::isSteppable() const
{
return true;
}
bool NumberInputType::stepMismatch(const String& value, double step) const
{
double doubleValue;
if (!parseToDoubleForNumberType(value, &doubleValue))
return false;
doubleValue = fabs(doubleValue - stepBase());
if (isinf(doubleValue))
return false;
if (doubleValue / pow(2.0, DBL_MANT_DIG) > step)
return false;
double remainder = fabs(doubleValue - step * round(doubleValue / step));
double computedAcceptableError = acceptableError(step);
return computedAcceptableError < remainder && remainder < (step - computedAcceptableError);
}
double NumberInputType::stepBase() const
{
return parseToDouble(element()->fastGetAttribute(minAttr), defaultStepBase());
}
double NumberInputType::stepBaseWithDecimalPlaces(unsigned* decimalPlaces) const
{
return parseToDoubleWithDecimalPlaces(element()->fastGetAttribute(minAttr), defaultStepBase(), decimalPlaces);
}
double NumberInputType::defaultStep() const
{
return numberDefaultStep;
}
double NumberInputType::stepScaleFactor() const
{
return numberStepScaleFactor;
}
void NumberInputType::handleKeydownEvent(KeyboardEvent* event)
{
handleKeydownEventForSpinButton(event);
if (!event->defaultHandled())
TextFieldInputType::handleKeydownEvent(event);
}
void NumberInputType::handleWheelEvent(WheelEvent* event)
{
handleWheelEventForSpinButton(event);
}
double NumberInputType::parseToDouble(const String& src, double defaultValue) const
{
double numberValue;
if (!parseToDoubleForNumberType(src, &numberValue))
return defaultValue;
ASSERT(isfinite(numberValue));
return numberValue;
}
double NumberInputType::parseToDoubleWithDecimalPlaces(const String& src, double defaultValue, unsigned *decimalPlaces) const
{
double numberValue;
if (!parseToDoubleForNumberTypeWithDecimalPlaces(src, &numberValue, decimalPlaces))
return defaultValue;
ASSERT(isfinite(numberValue));
return numberValue;
}
String NumberInputType::serialize(double value) const
{
if (!isfinite(value))
return String();
return serializeForNumberType(value);
}
double NumberInputType::acceptableError(double step) const
{
return step / pow(2.0, FLT_MANT_DIG);
}
void NumberInputType::handleBlurEvent()
{
element()->setFormControlValueMatchesRenderer(false);
element()->updateInnerTextValue();
}
static bool isE(UChar ch)
{
return ch == 'e' || ch == 'E';
}
String NumberInputType::visibleValue() const
{
String currentValue = element()->value();
if (currentValue.isEmpty())
return currentValue;
if (currentValue.find(isE) != notFound)
return currentValue;
double doubleValue = numeric_limits<double>::quiet_NaN();
unsigned decimalPlace;
parseToDoubleForNumberTypeWithDecimalPlaces(currentValue, &doubleValue, &decimalPlace);
return convertToLocalizedNumber(currentValue, decimalPlace);
}
String NumberInputType::convertFromVisibleValue(const String& visibleValue) const
{
if (visibleValue.isEmpty())
return visibleValue;
if (visibleValue.find(isE) != notFound)
return visibleValue;
return convertFromLocalizedNumber(visibleValue);
}
bool NumberInputType::isAcceptableValue(const String& proposedValue)
{
String standardValue = convertFromVisibleValue(proposedValue);
return standardValue.isEmpty() || parseToDoubleForNumberType(standardValue, 0);
}
String NumberInputType::sanitizeValue(const String& proposedValue) const
{
if (proposedValue.isEmpty())
return proposedValue;
return parseToDoubleForNumberType(proposedValue, 0) ? proposedValue : emptyAtom.string();
}
bool NumberInputType::hasUnacceptableValue()
{
return element()->renderer() && !isAcceptableValue(element()->innerTextValue());
}
bool NumberInputType::shouldRespectSpeechAttribute()
{
return true;
}
bool NumberInputType::supportsPlaceholder() const
{
return true;
}
bool NumberInputType::isNumberField() const
{
return true;
}
void NumberInputType::minOrMaxAttributeChanged()
{
InputType::minOrMaxAttributeChanged();
if (element()->renderer())
element()->renderer()->setNeedsLayoutAndPrefWidthsRecalc();
}
void NumberInputType::stepAttributeChanged()
{
InputType::stepAttributeChanged();
if (element()->renderer())
element()->renderer()->setNeedsLayoutAndPrefWidthsRecalc();
}
}