| Index: Source/core/html/forms/InputType.cpp
|
| diff --git a/Source/core/html/forms/InputType.cpp b/Source/core/html/forms/InputType.cpp
|
| index 238c38bb47055497971a10feca8e49da300a8989..e56509a3b7d903afb0a840024e54d14cd1dc38b2 100644
|
| --- a/Source/core/html/forms/InputType.cpp
|
| +++ b/Source/core/html/forms/InputType.cpp
|
| @@ -794,7 +794,7 @@ unsigned InputType::width() const
|
| return 0;
|
| }
|
|
|
| -void InputType::applyStep(int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState)
|
| +void InputType::applyStep(const Decimal& current, int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState)
|
| {
|
| StepRange stepRange(createStepRange(anyStepHandling));
|
| if (!stepRange.hasStep()) {
|
| @@ -802,38 +802,56 @@ void InputType::applyStep(int count, AnyStepHandling anyStepHandling, TextFieldE
|
| return;
|
| }
|
|
|
| - const Decimal current = parseToNumberOrNaN(element().value());
|
| - if (!current.isFinite()) {
|
| - exceptionState.throwDOMException(InvalidStateError, ExceptionMessages::notAFiniteNumber(current, "form element's current value"));
|
| - return;
|
| - }
|
| - Decimal newValue = current + stepRange.step() * count;
|
| - if (!newValue.isFinite()) {
|
| - exceptionState.throwDOMException(InvalidStateError, ExceptionMessages::notAFiniteNumber(newValue, "form element's new value"));
|
| - return;
|
| - }
|
| -
|
| - const Decimal acceptableErrorValue = stepRange.acceptableError();
|
| - if (newValue - stepRange.minimum() < -acceptableErrorValue) {
|
| - exceptionState.throwDOMException(InvalidStateError, "The form element's new value (" + newValue.toString() + ") would be lower than the minimum (" + stepRange.minimum().toString() + "), and snapping to the minimum would exceed the amount of acceptible error.");
|
| - return;
|
| - }
|
| - if (newValue < stepRange.minimum())
|
| - newValue = stepRange.minimum();
|
| + EventQueueScope scope;
|
| + const Decimal step = stepRange.step();
|
|
|
| const AtomicString& stepString = element().fastGetAttribute(stepAttr);
|
| - if (!equalIgnoringCase(stepString, "any"))
|
| - newValue = stepRange.alignValueForStep(current, newValue);
|
| + if (!equalIgnoringCase(stepString, "any") && stepRange.stepMismatch(current)) {
|
| + // Snap-to-step / clamping steps
|
| + // If the current value is not matched to step value:
|
| + // - The value should be the larger matched value nearest to 0 if count > 0
|
| + // e.g. <input type=number value=3 min=-100 step=3> -> 5
|
| + // - The value should be the smaller matched value nearest to 0 if count < 0
|
| + // e.g. <input type=number value=3 min=-100 step=3> -> 2
|
| + //
|
| +
|
| + ASSERT(!step.isZero());
|
| + Decimal newValue;
|
| + const Decimal base = stepRange.stepBase();
|
| + if (count < 0)
|
| + newValue = base + ((current - base) / step).floor() * step;
|
| + else if (count > 0)
|
| + newValue = base + ((current - base) / step).ceiling() * step;
|
| + else
|
| + newValue = current;
|
| +
|
| + if (newValue < stepRange.minimum())
|
| + newValue = stepRange.minimum();
|
| + if (newValue > stepRange.maximum())
|
| + newValue = stepRange.maximum();
|
| +
|
| + setValueAsDecimal(newValue, count == 1 || count == -1 ? DispatchChangeEvent : DispatchNoEvent, IGNORE_EXCEPTION);
|
| + if (count > 1) {
|
| + applyStep(newValue, count - 1, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
|
| + return;
|
| + }
|
| + if (count < -1) {
|
| + applyStep(newValue, count + 1, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
|
| + return;
|
| + }
|
| + } else {
|
| + Decimal newValue = current + stepRange.step() * count;
|
|
|
| - if (newValue - stepRange.maximum() > acceptableErrorValue) {
|
| - exceptionState.throwDOMException(InvalidStateError, "The form element's new value (" + newValue.toString() + ") would be higher than the maximum (" + stepRange.maximum().toString() + "), and snapping to the maximum would exceed the amount of acceptible error.");
|
| - return;
|
| - }
|
| - if (newValue > stepRange.maximum())
|
| - newValue = stepRange.maximum();
|
| + if (!equalIgnoringCase(stepString, "any"))
|
| + newValue = stepRange.alignValueForStep(current, newValue);
|
|
|
| - setValueAsDecimal(newValue, eventBehavior, exceptionState);
|
| + if (newValue > stepRange.maximum())
|
| + newValue = newValue - stepRange.step();
|
| + else if (newValue < stepRange.minimum())
|
| + newValue = newValue + stepRange.step();
|
|
|
| + setValueAsDecimal(newValue, eventBehavior, exceptionState);
|
| + }
|
| if (AXObjectCache* cache = element().document().existingAXObjectCache())
|
| cache->postNotification(&element(), AXObjectCache::AXValueChanged, true);
|
| }
|
| @@ -857,14 +875,15 @@ void InputType::stepUp(int n, ExceptionState& exceptionState)
|
| exceptionState.throwDOMException(InvalidStateError, "This form element is not steppable.");
|
| return;
|
| }
|
| - applyStep(n, RejectAny, DispatchNoEvent, exceptionState);
|
| + const Decimal current = parseToNumber(element().value(), 0);
|
| + applyStep(current, n, RejectAny, DispatchNoEvent, exceptionState);
|
| }
|
|
|
| void InputType::stepUpFromRenderer(int n)
|
| {
|
| - // The differences from stepUp()/stepDown():
|
| + // The only difference from stepUp()/stepDown() is the extra treatment
|
| + // of the current value before applying the step:
|
| //
|
| - // Difference 1: the current value
|
| // If the current value is not a number, including empty, the current value is assumed as 0.
|
| // * If 0 is in-range, and matches to step value
|
| // - The value should be the +step if n > 0
|
| @@ -888,13 +907,6 @@ void InputType::stepUpFromRenderer(int n)
|
| // - The value should be the maximum value if n < 0
|
| // - Nothing should happen if n > 0
|
| //
|
| - // Difference 2: clamping steps
|
| - // If the current value is not matched to step value:
|
| - // - The value should be the larger matched value nearest to 0 if n > 0
|
| - // e.g. <input type=number value=3 min=-100 step=3> -> 5
|
| - // - The value should be the smaler matched value nearest to 0 if n < 0
|
| - // e.g. <input type=number value=3 min=-100 step=3> -> 2
|
| - //
|
| // n is assumed as -n if step < 0.
|
|
|
| ASSERT(isSteppable());
|
| @@ -922,8 +934,7 @@ void InputType::stepUpFromRenderer(int n)
|
| else
|
| sign = 0;
|
|
|
| - String currentStringValue = element().value();
|
| - Decimal current = parseToNumberOrNaN(currentStringValue);
|
| + Decimal current = parseToNumberOrNaN(element().value());
|
| if (!current.isFinite()) {
|
| current = defaultValueForStepUp();
|
| const Decimal nextDiff = step * n;
|
| @@ -935,32 +946,9 @@ void InputType::stepUpFromRenderer(int n)
|
| }
|
| if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum())) {
|
| setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchChangeEvent, IGNORE_EXCEPTION);
|
| - } else {
|
| - if (stepMismatch(element().value())) {
|
| - ASSERT(!step.isZero());
|
| - const Decimal base = stepRange.stepBase();
|
| - Decimal newValue;
|
| - if (sign < 0)
|
| - newValue = base + ((current - base) / step).floor() * step;
|
| - else if (sign > 0)
|
| - newValue = base + ((current - base) / step).ceiling() * step;
|
| - else
|
| - newValue = current;
|
| -
|
| - if (newValue < stepRange.minimum())
|
| - newValue = stepRange.minimum();
|
| - if (newValue > stepRange.maximum())
|
| - newValue = stepRange.maximum();
|
| -
|
| - setValueAsDecimal(newValue, n == 1 || n == -1 ? DispatchChangeEvent : DispatchNoEvent, IGNORE_EXCEPTION);
|
| - if (n > 1)
|
| - applyStep(n - 1, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
|
| - else if (n < -1)
|
| - applyStep(n + 1, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
|
| - } else {
|
| - applyStep(n, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
|
| - }
|
| + return;
|
| }
|
| + applyStep(current, n, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
|
| }
|
|
|
| void InputType::countUsageIfVisible(UseCounter::Feature feature) const
|
|
|