root/Source/core/html/forms/NumberInputType.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. calculateRenderSize
  2. create
  3. countUsage
  4. formControlType
  5. setValue
  6. valueAsDouble
  7. setValueAsDouble
  8. setValueAsDecimal
  9. typeMismatchFor
  10. typeMismatch
  11. createStepRange
  12. sizeShouldIncludeDecoration
  13. isSteppable
  14. handleKeydownEvent
  15. parseToNumber
  16. serialize
  17. isE
  18. localizeValue
  19. visibleValue
  20. convertFromVisibleValue
  21. sanitizeValue
  22. hasBadInput
  23. badInputText
  24. rangeOverflowText
  25. rangeUnderflowText
  26. shouldRespectSpeechAttribute
  27. supportsPlaceholder
  28. isNumberField
  29. minOrMaxAttributeChanged
  30. stepAttributeChanged
  31. supportsSelectionAPI

/*
 * Copyright (C) 2010 Google Inc. All rights reserved.
 * Copyright (C) 2011 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "core/html/forms/NumberInputType.h"

#include "HTMLNames.h"
#include "InputTypeNames.h"
#include "bindings/v8/ExceptionState.h"
#include "core/dom/ExceptionCode.h"
#include "core/events/KeyboardEvent.h"
#include "core/html/HTMLInputElement.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "core/rendering/RenderTextControl.h"
#include "platform/text/PlatformLocale.h"
#include "wtf/MathExtras.h"
#include "wtf/PassOwnPtr.h"
#include <limits>

namespace WebCore {

using blink::WebLocalizedString;
using namespace HTMLNames;
using namespace std;

static const int numberDefaultStep = 1;
static const int numberDefaultStepBase = 0;
static const int numberStepScaleFactor = 1;

struct RealNumberRenderSize {
    unsigned sizeBeforeDecimalPoint;
    unsigned sizeAfteDecimalPoint;

    RealNumberRenderSize(unsigned before, unsigned after)
        : sizeBeforeDecimalPoint(before)
        , sizeAfteDecimalPoint(after)
    {
    }

    RealNumberRenderSize max(const RealNumberRenderSize& other) const
    {
        return RealNumberRenderSize(
            std::max(sizeBeforeDecimalPoint, other.sizeBeforeDecimalPoint),
            std::max(sizeAfteDecimalPoint, other.sizeAfteDecimalPoint));
    }
};

static RealNumberRenderSize calculateRenderSize(const Decimal& value)
{
    ASSERT(value.isFinite());
    const unsigned sizeOfDigits = String::number(value.value().coefficient()).length();
    const unsigned sizeOfSign = value.isNegative() ? 1 : 0;
    const int exponent = value.exponent();
    if (exponent >= 0)
        return RealNumberRenderSize(sizeOfSign + sizeOfDigits, 0);

    const int sizeBeforeDecimalPoint = exponent + sizeOfDigits;
    if (sizeBeforeDecimalPoint > 0) {
        // In case of "123.456"
        return RealNumberRenderSize(sizeOfSign + sizeBeforeDecimalPoint, sizeOfDigits - sizeBeforeDecimalPoint);
    }

    // In case of "0.00012345"
    const unsigned sizeOfZero = 1;
    const unsigned numberOfZeroAfterDecimalPoint = -sizeBeforeDecimalPoint;
    return RealNumberRenderSize(sizeOfSign + sizeOfZero , numberOfZeroAfterDecimalPoint + sizeOfDigits);
}

PassRefPtr<InputType> NumberInputType::create(HTMLInputElement& element)
{
    return adoptRef(new NumberInputType(element));
}

void NumberInputType::countUsage()
{
    countUsageIfVisible(UseCounter::InputTypeNumber);
}

const AtomicString& NumberInputType::formControlType() const
{
    return InputTypeNames::number;
}

void NumberInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
{
    if (!valueChanged && sanitizedValue.isEmpty() && !element().innerTextValue().isEmpty())
        element().updateView();
    TextFieldInputType::setValue(sanitizedValue, valueChanged, eventBehavior);
}

double NumberInputType::valueAsDouble() const
{
    return parseToDoubleForNumberType(element().value());
}

void NumberInputType::setValueAsDouble(double newValue, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState) const
{
    element().setValue(serializeForNumberType(newValue), eventBehavior);
}

void NumberInputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState) const
{
    element().setValue(serializeForNumberType(newValue), eventBehavior);
}

bool NumberInputType::typeMismatchFor(const String& value) const
{
    return !value.isEmpty() && !std::isfinite(parseToDoubleForNumberType(value));
}

bool NumberInputType::typeMismatch() const
{
    ASSERT(!typeMismatchFor(element().value()));
    return false;
}

StepRange NumberInputType::createStepRange(AnyStepHandling anyStepHandling) const
{
    DEFINE_STATIC_LOCAL(const StepRange::StepDescription, stepDescription, (numberDefaultStep, numberDefaultStepBase, numberStepScaleFactor));
    const Decimal doubleMax = Decimal::fromDouble(numeric_limits<double>::max());
    return InputType::createStepRange(anyStepHandling, numberDefaultStepBase, -doubleMax, doubleMax, stepDescription);
}

bool NumberInputType::sizeShouldIncludeDecoration(int defaultSize, int& preferredSize) const
{
    preferredSize = defaultSize;

    const String stepString = element().fastGetAttribute(stepAttr);
    if (equalIgnoringCase(stepString, "any"))
        return false;

    const Decimal minimum = parseToDecimalForNumberType(element().fastGetAttribute(minAttr));
    if (!minimum.isFinite())
        return false;

    const Decimal maximum = parseToDecimalForNumberType(element().fastGetAttribute(maxAttr));
    if (!maximum.isFinite())
        return false;

    const Decimal step = parseToDecimalForNumberType(stepString, 1);
    ASSERT(step.isFinite());

    RealNumberRenderSize size = calculateRenderSize(minimum).max(calculateRenderSize(maximum).max(calculateRenderSize(step)));

    preferredSize = size.sizeBeforeDecimalPoint + size.sizeAfteDecimalPoint + (size.sizeAfteDecimalPoint ? 1 : 0);

    return true;
}

bool NumberInputType::isSteppable() const
{
    return true;
}

void NumberInputType::handleKeydownEvent(KeyboardEvent* event)
{
    handleKeydownEventForSpinButton(event);
    if (!event->defaultHandled())
        TextFieldInputType::handleKeydownEvent(event);
}

Decimal NumberInputType::parseToNumber(const String& src, const Decimal& defaultValue) const
{
    return parseToDecimalForNumberType(src, defaultValue);
}

String NumberInputType::serialize(const Decimal& value) const
{
    if (!value.isFinite())
        return String();
    return serializeForNumberType(value);
}

static bool isE(UChar ch)
{
    return ch == 'e' || ch == 'E';
}

String NumberInputType::localizeValue(const String& proposedValue) const
{
    if (proposedValue.isEmpty())
        return proposedValue;
    // We don't localize scientific notations.
    if (proposedValue.find(isE) != kNotFound)
        return proposedValue;
    return element().locale().convertToLocalizedNumber(proposedValue);
}

String NumberInputType::visibleValue() const
{
    return localizeValue(element().value());
}

String NumberInputType::convertFromVisibleValue(const String& visibleValue) const
{
    if (visibleValue.isEmpty())
        return visibleValue;
    // We don't localize scientific notations.
    if (visibleValue.find(isE) != kNotFound)
        return visibleValue;
    return element().locale().convertFromLocalizedNumber(visibleValue);
}

String NumberInputType::sanitizeValue(const String& proposedValue) const
{
    if (proposedValue.isEmpty())
        return proposedValue;
    return std::isfinite(parseToDoubleForNumberType(proposedValue)) ? proposedValue : emptyString();
}

bool NumberInputType::hasBadInput() const
{
    String standardValue = convertFromVisibleValue(element().innerTextValue());
    return !standardValue.isEmpty() && !std::isfinite(parseToDoubleForNumberType(standardValue));
}

String NumberInputType::badInputText() const
{
    return locale().queryString(WebLocalizedString::ValidationBadInputForNumber);
}

String NumberInputType::rangeOverflowText(const Decimal& maximum) const
{
    return locale().queryString(WebLocalizedString::ValidationRangeOverflow, localizeValue(serialize(maximum)));
}

String NumberInputType::rangeUnderflowText(const Decimal& minimum) const
{
    return locale().queryString(WebLocalizedString::ValidationRangeUnderflow, localizeValue(serialize(minimum)));
}

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();
}

bool NumberInputType::supportsSelectionAPI() const
{
    return false;
}

} // namespace WebCore

/* [<][>][^][v][top][bottom][index][help] */