std::pair<String, String> InputType::validationMessage(const InputTypeView& inputTypeView) const { const String value = element().value(); // The order of the following checks is meaningful. e.g. We'd like to show the // badInput message even if the control has other validation errors. if (inputTypeView.hasBadInput()) return std::make_pair(badInputText(), emptyString()); if (valueMissing(value)) return std::make_pair(valueMissingText(), emptyString()); if (typeMismatch()) return std::make_pair(typeMismatchText(), emptyString()); if (patternMismatch(value)) { // https://html.spec.whatwg.org/multipage/forms.html#attr-input-pattern // When an input element has a pattern attribute specified, authors // should include a title attribute to give a description of the // pattern. User agents may use the contents of this attribute, if it // is present, when informing the user that the pattern is not matched return std::make_pair(locale().queryString(WebLocalizedString::ValidationPatternMismatch), element().fastGetAttribute(titleAttr).getString()); } if (element().tooLong()) return std::make_pair(locale().validationMessageTooLongText(value.length(), element().maxLength()), emptyString()); if (element().tooShort()) return std::make_pair(locale().validationMessageTooShortText(value.length(), element().minLength()), emptyString()); if (!isSteppable()) return std::make_pair(emptyString(), emptyString()); const Decimal numericValue = parseToNumberOrNaN(value); if (!numericValue.isFinite()) return std::make_pair(emptyString(), emptyString()); StepRange stepRange(createStepRange(RejectAny)); if (numericValue < stepRange.minimum()) return std::make_pair(rangeUnderflowText(stepRange.minimum()), emptyString()); if (numericValue > stepRange.maximum()) return std::make_pair(rangeOverflowText(stepRange.maximum()), emptyString()); if (stepRange.stepMismatch(numericValue)) { DCHECK(stepRange.hasStep()); Decimal candidate1 = stepRange.clampValue(numericValue); String localizedCandidate1 = localizeValue(serialize(candidate1)); Decimal candidate2 = candidate1 < numericValue ? candidate1 + stepRange.step() : candidate1 - stepRange.step(); if (!candidate2.isFinite() || candidate2 < stepRange.minimum() || candidate2 > stepRange.maximum()) return std::make_pair(locale().queryString(WebLocalizedString::ValidationStepMismatchCloseToLimit, localizedCandidate1), emptyString()); String localizedCandidate2 = localizeValue(serialize(candidate2)); if (candidate1 < candidate2) return std::make_pair(locale().queryString(WebLocalizedString::ValidationStepMismatch, localizedCandidate1, localizedCandidate2), emptyString()); return std::make_pair(locale().queryString(WebLocalizedString::ValidationStepMismatch, localizedCandidate2, localizedCandidate1), emptyString()); } return std::make_pair(emptyString(), emptyString()); }
void SliderThumbElement::setPositionFromPoint(const LayoutPoint& absolutePoint) { RefPtr<HTMLInputElement> input = hostInput(); if (!input || !input->renderer() || !renderBox()) return; HTMLElement* trackElement = input->sliderTrackElement(); if (!trackElement->renderBox()) return; // Do all the tracking math relative to the input's renderer's box. RenderBox& inputRenderer = downcast<RenderBox>(*input->renderer()); RenderBox& trackRenderer = *trackElement->renderBox(); bool isVertical = hasVerticalAppearance(input.get()); bool isLeftToRightDirection = renderBox()->style().isLeftToRightDirection(); LayoutPoint offset(inputRenderer.absoluteToLocal(absolutePoint, UseTransforms)); FloatRect trackBoundingBox = trackRenderer.localToContainerQuad(FloatRect(0, 0, trackRenderer.width(), trackRenderer.height()), &inputRenderer).enclosingBoundingBox(); LayoutUnit trackLength; LayoutUnit position; if (isVertical) { trackLength = trackRenderer.contentHeight() - renderBox()->height(); position = offset.y() - renderBox()->height() / 2 - trackBoundingBox.y() - renderBox()->marginBottom(); } else { trackLength = trackRenderer.contentWidth() - renderBox()->width(); position = offset.x() - renderBox()->width() / 2 - trackBoundingBox.x(); position -= isLeftToRightDirection ? renderBox()->marginLeft() : renderBox()->marginRight(); } position = std::max<LayoutUnit>(0, std::min(position, trackLength)); const Decimal ratio = Decimal::fromDouble(static_cast<double>(position) / trackLength); const Decimal fraction = isVertical || !isLeftToRightDirection ? Decimal(1) - ratio : ratio; StepRange stepRange(input->createStepRange(RejectAny)); Decimal value = stepRange.clampValue(stepRange.valueFromProportion(fraction)); #if ENABLE(DATALIST_ELEMENT) const LayoutUnit snappingThreshold = renderer()->theme().sliderTickSnappingThreshold(); if (snappingThreshold > 0) { Decimal closest = input->findClosestTickMarkValue(value); if (closest.isFinite()) { double closestFraction = stepRange.proportionFromValue(closest).toDouble(); double closestRatio = isVertical || !isLeftToRightDirection ? 1.0 - closestFraction : closestFraction; LayoutUnit closestPosition = trackLength * closestRatio; if ((closestPosition - position).abs() <= snappingThreshold) value = closest; } } #endif String valueString = serializeForNumberType(value); if (valueString == input->value()) return; // FIXME: This is no longer being set from renderer. Consider updating the method name. input->setValueFromRenderer(valueString); if (renderer()) renderer()->setNeedsLayout(); }
void RangeInputType::handleKeydownEvent(KeyboardEvent* event) { if (element()->isDisabledOrReadOnly()) return; const String& key = event->keyIdentifier(); const Decimal current = parseToNumberOrNaN(element()->value()); ASSERT(current.isFinite()); StepRange stepRange(createStepRange(RejectAny)); // FIXME: We can't use stepUp() for the step value "any". So, we increase // or decrease the value by 1/100 of the value range. Is it reasonable? const Decimal step = equalIgnoringCase(element()->fastGetAttribute(stepAttr), "any") ? (stepRange.maximum() - stepRange.minimum()) / 100 : stepRange.step(); const Decimal bigStep = max((stepRange.maximum() - stepRange.minimum()) / 10, step); bool isVertical = false; if (element()->renderer()) { ControlPart part = element()->renderer()->style()->appearance(); isVertical = part == SliderVerticalPart || part == MediaVolumeSliderPart; } Decimal newValue; if (key == "Up") newValue = current + step; else if (key == "Down") newValue = current - step; else if (key == "Left") newValue = isVertical ? current + step : current - step; else if (key == "Right") newValue = isVertical ? current - step : current + step; else if (key == "PageUp") newValue = current + bigStep; else if (key == "PageDown") newValue = current - bigStep; else if (key == "Home") newValue = isVertical ? stepRange.maximum() : stepRange.minimum(); else if (key == "End") newValue = isVertical ? stepRange.minimum() : stepRange.maximum(); else return; // Did not match any key binding. newValue = stepRange.clampValue(newValue); if (newValue != current) { EventQueueScope scope; ExceptionCode ec; TextFieldEventBehavior eventBehavior = DispatchChangeEvent; setValueAsDecimal(newValue, eventBehavior, ec); if (AXObjectCache::accessibilityEnabled()) element()->document()->axObjectCache()->postNotification(element(), AXObjectCache::AXValueChanged, true); element()->dispatchFormControlChangeEvent(); } event->setDefaultHandled(); }
void SliderThumbElement::setPositionFromPoint(const LayoutPoint& point) { RefPtrWillBeRawPtr<HTMLInputElement> input(hostInput()); Element* trackElement = input->closedShadowRoot()->getElementById(ShadowElementNames::sliderTrack()); if (!input->layoutObject() || !layoutBox() || !trackElement->layoutBox()) return; LayoutPoint offset = roundedLayoutPoint(input->layoutObject()->absoluteToLocal(FloatPoint(point), UseTransforms)); bool isVertical = hasVerticalAppearance(input.get()); bool isLeftToRightDirection = layoutBox()->style()->isLeftToRightDirection(); LayoutUnit trackSize; LayoutUnit position; LayoutUnit currentPosition; // We need to calculate currentPosition from absolute points becaue the // renderer for this node is usually on a layer and layoutBox()->x() and // y() are unusable. // FIXME: This should probably respect transforms. LayoutPoint absoluteThumbOrigin = layoutBox()->absoluteBoundingBoxRectIgnoringTransforms().location(); LayoutPoint absoluteSliderContentOrigin = roundedLayoutPoint(input->layoutObject()->localToAbsolute()); IntRect trackBoundingBox = trackElement->layoutObject()->absoluteBoundingBoxRectIgnoringTransforms(); IntRect inputBoundingBox = input->layoutObject()->absoluteBoundingBoxRectIgnoringTransforms(); if (isVertical) { trackSize = trackElement->layoutBox()->contentHeight() - layoutBox()->size().height(); position = offset.y() - layoutBox()->size().height() / 2 - trackBoundingBox.y() + inputBoundingBox.y() - layoutBox()->marginBottom(); currentPosition = absoluteThumbOrigin.y() - absoluteSliderContentOrigin.y(); } else { trackSize = trackElement->layoutBox()->contentWidth() - layoutBox()->size().width(); position = offset.x() - layoutBox()->size().width() / 2 - trackBoundingBox.x() + inputBoundingBox.x(); position -= isLeftToRightDirection ? layoutBox()->marginLeft() : layoutBox()->marginRight(); currentPosition = absoluteThumbOrigin.x() - absoluteSliderContentOrigin.x(); } position = std::max<LayoutUnit>(0, std::min(position, trackSize)); const Decimal ratio = Decimal::fromDouble(static_cast<double>(position) / trackSize); const Decimal fraction = isVertical || !isLeftToRightDirection ? Decimal(1) - ratio : ratio; StepRange stepRange(input->createStepRange(RejectAny)); Decimal value = stepRange.clampValue(stepRange.valueFromProportion(fraction)); Decimal closest = input->findClosestTickMarkValue(value); if (closest.isFinite()) { double closestFraction = stepRange.proportionFromValue(closest).toDouble(); double closestRatio = isVertical || !isLeftToRightDirection ? 1.0 - closestFraction : closestFraction; LayoutUnit closestPosition = trackSize * closestRatio; const LayoutUnit snappingThreshold = 5; if ((closestPosition - position).abs() <= snappingThreshold) value = closest; } String valueString = serializeForNumberType(value); if (valueString == input->value()) return; // FIXME: This is no longer being set from renderer. Consider updating the method name. input->setValueFromRenderer(valueString); if (layoutObject()) layoutObject()->setNeedsLayoutAndFullPaintInvalidation(LayoutInvalidationReason::SliderValueChanged); }
bool InputType::isOutOfRange(const String& value) const { if (!isSteppable()) return false; const Decimal numericValue = parseToNumberOrNaN(value); if (!numericValue.isFinite()) return true; StepRange stepRange(createStepRange(RejectAny)); return numericValue < stepRange.minimum() || numericValue > stepRange.maximum(); }
bool InputType::isOutOfRange(const String& value) const { if (!isSteppable()) return false; // This function should return true if either validity.rangeUnderflow or // validity.rangeOverflow are true. // If the INPUT has no value, they are false. const Decimal numericValue = parseToNumberOrNaN(value); if (!numericValue.isFinite()) return false; StepRange stepRange(createStepRange(RejectAny)); return stepRange.hasRangeLimitations() && (numericValue < stepRange.minimum() || numericValue > stepRange.maximum()); }
bool InputType::isInRange(const String& value) const { if (!isSteppable()) return false; // This function should return true if both of validity.rangeUnderflow and // validity.rangeOverflow are false. // If the INPUT has no value, they are false. const Decimal numericValue = parseToNumberOrNaN(value); if (!numericValue.isFinite()) return true; StepRange stepRange(createStepRange(RejectAny)); return numericValue >= stepRange.minimum() && numericValue <= stepRange.maximum(); }
void InputType::applyStep(int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior, ExceptionCode& ec) { StepRange stepRange(createStepRange(anyStepHandling)); if (!stepRange.hasStep()) { ec = INVALID_STATE_ERR; return; } const Decimal current = parseToNumberOrNaN(element().value()); if (!current.isFinite()) { ec = INVALID_STATE_ERR; return; } Decimal newValue = current + stepRange.step() * count; if (!newValue.isFinite()) { ec = INVALID_STATE_ERR; return; } const Decimal acceptableErrorValue = stepRange.acceptableError(); if (newValue - stepRange.minimum() < -acceptableErrorValue) { ec = INVALID_STATE_ERR; return; } if (newValue < stepRange.minimum()) newValue = stepRange.minimum(); const AtomicString& stepString = element().fastGetAttribute(stepAttr); if (!equalIgnoringCase(stepString, "any")) newValue = stepRange.alignValueForStep(current, newValue); if (newValue - stepRange.maximum() > acceptableErrorValue) { ec = INVALID_STATE_ERR; return; } if (newValue > stepRange.maximum()) newValue = stepRange.maximum(); setValueAsDecimal(newValue, eventBehavior, ec); if (AXObjectCache* cache = element().document().existingAXObjectCache()) cache->postNotification(&element(), AXObjectCache::AXValueChanged); }
String InputType::validationMessage() const { String value = element().value(); // The order of the following checks is meaningful. e.g. We'd like to show the // badInput message even if the control has other validation errors. if (hasBadInput()) return badInputText(); if (valueMissing(value)) return valueMissingText(); if (typeMismatch()) return typeMismatchText(); if (patternMismatch(value)) return validationMessagePatternMismatchText(); if (element().tooLong()) return validationMessageTooLongText(numGraphemeClusters(value), element().maxLength()); if (!isSteppable()) return emptyString(); const Decimal numericValue = parseToNumberOrNaN(value); if (!numericValue.isFinite()) return emptyString(); StepRange stepRange(createStepRange(RejectAny)); if (numericValue < stepRange.minimum()) return validationMessageRangeUnderflowText(serialize(stepRange.minimum())); if (numericValue > stepRange.maximum()) return validationMessageRangeOverflowText(serialize(stepRange.maximum())); if (stepRange.stepMismatch(numericValue)) { const String stepString = stepRange.hasStep() ? serializeForNumberType(stepRange.step() / stepRange.stepScaleFactor()) : emptyString(); return validationMessageStepMismatchText(serialize(stepRange.stepBase()), stepString); } return emptyString(); }
void InputType::stepUpFromRenderer(int n) { // The differences from stepUp()/stepDown(): // // 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 // - The value should be the -step if n < 0 // If -step or +step is out of range, new value should be 0. // * If 0 is smaller than the minimum value // - The value should be the minimum value for any n // * If 0 is larger than the maximum value // - The value should be the maximum value for any n // * If 0 is in-range, but not matched to step value // - The value should be the larger matched value nearest to 0 if n > 0 // e.g. <input type=number min=-100 step=3> -> 2 // - The value should be the smaler matched value nearest to 0 if n < 0 // e.g. <input type=number min=-100 step=3> -> -1 // As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time". // As for datetime type, the current value is assumed as "the current date/time in UTC". // If the current value is smaller than the minimum value: // - The value should be the minimum value if n > 0 // - Nothing should happen if n < 0 // If the current value is larger than the maximum value: // - 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()); if (!isSteppable()) return; ASSERT(n); if (!n) return; StepRange stepRange(createStepRange(AnyIsDefaultStep)); // FIXME: Not any changes after stepping, even if it is an invalid value, may be better. // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => "foo") if (!stepRange.hasStep()) return; EventQueueScope scope; const Decimal step = stepRange.step(); int sign; if (step > 0) sign = n; else if (step < 0) sign = -n; else sign = 0; String currentStringValue = element().value(); Decimal current = parseToNumberOrNaN(currentStringValue); if (!current.isFinite()) { current = defaultValueForStepUp(); const Decimal nextDiff = step * n; if (current < stepRange.minimum() - nextDiff) current = stepRange.minimum() - nextDiff; if (current > stepRange.maximum() - nextDiff) current = stepRange.maximum() - nextDiff; setValueAsDecimal(current, DispatchNoEvent, IGNORE_EXCEPTION); } if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum())) setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchInputAndChangeEvent, 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 ? DispatchInputAndChangeEvent : DispatchNoEvent, IGNORE_EXCEPTION); if (n > 1) applyStep(n - 1, AnyIsDefaultStep, DispatchInputAndChangeEvent, IGNORE_EXCEPTION); else if (n < -1) applyStep(n + 1, AnyIsDefaultStep, DispatchInputAndChangeEvent, IGNORE_EXCEPTION); } else applyStep(n, AnyIsDefaultStep, DispatchInputAndChangeEvent, IGNORE_EXCEPTION); } }
bool InputType::getAllowedValueStep(Decimal* step) const { StepRange stepRange(createStepRange(RejectAny)); *step = stepRange.step(); return stepRange.hasStep(); }
String RangeInputType::sanitizeValue(const String& proposedValue) const { StepRange stepRange(createStepRange(RejectAny)); const Decimal proposedNumericValue = parseToNumber(proposedValue, stepRange.defaultValue()); return serializeForNumberType(stepRange.clampValue(proposedNumericValue)); }
void InputType::stepUpFromLayoutObject(int n) { // The only difference from stepUp()/stepDown() is the extra treatment // of the current value before applying the step: // // 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 // - The value should be the -step if n < 0 // If -step or +step is out of range, new value should be 0. // * If 0 is smaller than the minimum value // - The value should be the minimum value for any n // * If 0 is larger than the maximum value // - The value should be the maximum value for any n // * If 0 is in-range, but not matched to step value // - The value should be the larger matched value nearest to 0 if n > 0 // e.g. <input type=number min=-100 step=3> -> 2 // - The value should be the smaler matched value nearest to 0 if n < 0 // e.g. <input type=number min=-100 step=3> -> -1 // As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time". // As for datetime type, the current value is assumed as "the current date/time in UTC". // If the current value is smaller than the minimum value: // - The value should be the minimum value if n > 0 // - Nothing should happen if n < 0 // If the current value is larger than the maximum value: // - The value should be the maximum value if n < 0 // - Nothing should happen if n > 0 // // n is assumed as -n if step < 0. DCHECK(isSteppable()); if (!isSteppable()) return; DCHECK(n); if (!n) return; StepRange stepRange(createStepRange(AnyIsDefaultStep)); // FIXME: Not any changes after stepping, even if it is an invalid value, may be better. // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => "foo") if (!stepRange.hasStep()) return; EventQueueScope scope; const Decimal step = stepRange.step(); int sign; if (step > 0) sign = n; else if (step < 0) sign = -n; else sign = 0; Decimal current = parseToNumberOrNaN(element().value()); if (!current.isFinite()) { current = defaultValueForStepUp(); const Decimal nextDiff = step * n; if (current < stepRange.minimum() - nextDiff) current = stepRange.minimum() - nextDiff; if (current > stepRange.maximum() - nextDiff) current = stepRange.maximum() - nextDiff; setValueAsDecimal(current, DispatchNoEvent, IGNORE_EXCEPTION); } if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum())) { setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchChangeEvent, IGNORE_EXCEPTION); return; } if ((sign > 0 && current >= stepRange.maximum()) || (sign < 0 && current <= stepRange.minimum())) return; applyStep(current, n, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION); }
void InputType::applyStep(const Decimal& current, int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState) { // https://html.spec.whatwg.org/multipage/forms.html#dom-input-stepup StepRange stepRange(createStepRange(anyStepHandling)); // 2. If the element has no allowed value step, then throw an // InvalidStateError exception, and abort these steps. if (!stepRange.hasStep()) { exceptionState.throwDOMException(InvalidStateError, "This form element does not have an allowed value step."); return; } // 3. If the element has a minimum and a maximum and the minimum is greater // than the maximum, then abort these steps. if (stepRange.minimum() > stepRange.maximum()) return; // 4. If the element has a minimum and a maximum and there is no value // greater than or equal to the element's minimum and less than or equal to // the element's maximum that, when subtracted from the step base, is an // integral multiple of the allowed value step, then abort these steps. Decimal alignedMaximum = stepRange.stepSnappedMaximum(); if (!alignedMaximum.isFinite()) return; Decimal base = stepRange.stepBase(); Decimal step = stepRange.step(); EventQueueScope scope; Decimal newValue = current; const AtomicString& stepString = element().fastGetAttribute(stepAttr); 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 // DCHECK(!step.isZero()); if (count < 0) { newValue = base + ((newValue - base) / step).floor() * step; ++count; } else if (count > 0) { newValue = base + ((newValue - base) / step).ceil() * step; --count; } } newValue = newValue + stepRange.step() * count; if (!equalIgnoringCase(stepString, "any")) newValue = stepRange.alignValueForStep(current, newValue); // 7. If the element has a minimum, and value is less than that minimum, // then set value to the smallest value that, when subtracted from the step // base, is an integral multiple of the allowed value step, and that is more // than or equal to minimum. // 8. If the element has a maximum, and value is greater than that maximum, // then set value to the largest value that, when subtracted from the step // base, is an integral multiple of the allowed value step, and that is less // than or equal to maximum. if (newValue > stepRange.maximum()) { newValue = alignedMaximum; } else if (newValue < stepRange.minimum()) { const Decimal alignedMinimum = base + ((stepRange.minimum() - base) / step).ceil() * step; DCHECK_GE(alignedMinimum, stepRange.minimum()); newValue = alignedMinimum; } // 9. Let value as string be the result of running the algorithm to convert // a number to a string, as defined for the input element's type attribute's // current state, on value. // 10. Set the value of the element to value as string. setValueAsDecimal(newValue, eventBehavior, exceptionState); if (AXObjectCache* cache = element().document().existingAXObjectCache()) cache->handleValueChanged(&element()); }
void RangeInputType::handleKeydownEvent(KeyboardEvent* event) { if (element().isDisabledOrReadOnly()) return; const String& key = event->key(); const Decimal current = parseToNumberOrNaN(element().value()); DCHECK(current.isFinite()); StepRange stepRange(createStepRange(RejectAny)); // FIXME: We can't use stepUp() for the step value "any". So, we increase // or decrease the value by 1/100 of the value range. Is it reasonable? const Decimal step = equalIgnoringCase(element().fastGetAttribute(stepAttr), "any") ? (stepRange.maximum() - stepRange.minimum()) / 100 : stepRange.step(); const Decimal bigStep = std::max((stepRange.maximum() - stepRange.minimum()) / 10, step); TextDirection dir = LTR; bool isVertical = false; if (element().layoutObject()) { dir = computedTextDirection(); ControlPart part = element().layoutObject()->style()->appearance(); isVertical = part == SliderVerticalPart; } Decimal newValue; if (key == "ArrowUp") newValue = current + step; else if (key == "ArrowDown") newValue = current - step; else if (key == "ArrowLeft") newValue = (isVertical || dir == RTL) ? current + step : current - step; else if (key == "ArrowRight") newValue = (isVertical || dir == RTL) ? current - step : current + step; else if (key == "PageUp") newValue = current + bigStep; else if (key == "PageDown") newValue = current - bigStep; else if (key == "Home") newValue = isVertical ? stepRange.maximum() : stepRange.minimum(); else if (key == "End") newValue = isVertical ? stepRange.minimum() : stepRange.maximum(); else return; // Did not match any key binding. newValue = stepRange.clampValue(newValue); if (newValue != current) { EventQueueScope scope; TextFieldEventBehavior eventBehavior = DispatchInputAndChangeEvent; setValueAsDecimal(newValue, eventBehavior, IGNORE_EXCEPTION); if (AXObjectCache* cache = element().document().existingAXObjectCache()) cache->handleValueChanged(&element()); } event->setDefaultHandled(); }