2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2004-2018 Apple Inc. All rights reserved.
6 * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7 * Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
8 * Copyright (C) 2009, 2010, 2011, 2012 Google Inc. All rights reserved.
9 * Copyright (C) 2012 Samsung Electronics. All rights reserved.
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
21 * You should have received a copy of the GNU Library General Public License
22 * along with this library; see the file COPYING.LIB. If not, write to
23 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
29 #include "InputType.h"
31 #include "AXObjectCache.h"
32 #include "BeforeTextInsertedEvent.h"
33 #include "ButtonInputType.h"
34 #include "CheckboxInputType.h"
35 #include "ColorInputType.h"
36 #include "DOMFormData.h"
37 #include "DateComponents.h"
38 #include "DateInputType.h"
39 #include "DateTimeLocalInputType.h"
41 #include "DocumentInlines.h"
42 #include "ElementInlines.h"
43 #include "EmailInputType.h"
44 #include "EventNames.h"
45 #include "FileInputType.h"
47 #include "FormController.h"
48 #include "HTMLFormElement.h"
49 #include "HTMLInputElement.h"
50 #include "HTMLNames.h"
51 #include "HTMLParserIdioms.h"
52 #include "HiddenInputType.h"
53 #include "ImageInputType.h"
54 #include "InputTypeNames.h"
55 #include "KeyboardEvent.h"
56 #include "LocalizedStrings.h"
57 #include "MonthInputType.h"
58 #include "NodeRenderStyle.h"
59 #include "NumberInputType.h"
61 #include "PasswordInputType.h"
62 #include "RadioInputType.h"
63 #include "RangeInputType.h"
64 #include "RenderElement.h"
65 #include "RenderTheme.h"
66 #include "ResetInputType.h"
67 #include "ScopedEventQueue.h"
68 #include "SearchInputType.h"
69 #include "SelectionRestorationMode.h"
71 #include "ShadowRoot.h"
72 #include "StepRange.h"
73 #include "SubmitInputType.h"
74 #include "TelephoneInputType.h"
75 #include "TextControlInnerElements.h"
76 #include "TextInputType.h"
77 #include "TimeInputType.h"
78 #include "URLInputType.h"
79 #include "WeekInputType.h"
81 #include <wtf/Assertions.h>
82 #include <wtf/RobinHoodHashMap.h>
83 #include <wtf/text/StringHash.h>
84 #include <wtf/text/TextBreakIterator.h>
88 using namespace HTMLNames;
90 typedef bool (Settings::*InputTypeConditionalFunction)() const;
91 typedef const AtomString& (*InputTypeNameFunction)();
92 typedef Ref<InputType> (*InputTypeFactoryFunction)(HTMLInputElement&);
93 typedef MemoryCompactLookupOnlyRobinHoodHashMap<AtomString, std::pair<InputTypeConditionalFunction, InputTypeFactoryFunction>> InputTypeFactoryMap;
95 template<class T> static Ref<InputType> createInputType(HTMLInputElement& element)
97 return adoptRef(*new T(element));
100 static InputTypeFactoryMap createInputTypeFactoryMap()
102 static const struct InputTypes {
103 InputTypeConditionalFunction conditionalFunction;
104 InputTypeNameFunction nameFunction;
105 InputTypeFactoryFunction factoryFunction;
107 { nullptr, &InputTypeNames::button, &createInputType<ButtonInputType> },
108 { nullptr, &InputTypeNames::checkbox, &createInputType<CheckboxInputType> },
109 #if ENABLE(INPUT_TYPE_COLOR)
110 { &Settings::inputTypeColorEnabled, &InputTypeNames::color, &createInputType<ColorInputType> },
112 #if ENABLE(INPUT_TYPE_DATE)
113 { &Settings::inputTypeDateEnabled, &InputTypeNames::date, &createInputType<DateInputType> },
115 #if ENABLE(INPUT_TYPE_DATETIMELOCAL)
116 { &Settings::inputTypeDateTimeLocalEnabled, &InputTypeNames::datetimelocal, &createInputType<DateTimeLocalInputType> },
118 { nullptr, &InputTypeNames::email, &createInputType<EmailInputType> },
119 { nullptr, &InputTypeNames::file, &createInputType<FileInputType> },
120 { nullptr, &InputTypeNames::hidden, &createInputType<HiddenInputType> },
121 { nullptr, &InputTypeNames::image, &createInputType<ImageInputType> },
122 #if ENABLE(INPUT_TYPE_MONTH)
123 { &Settings::inputTypeMonthEnabled, &InputTypeNames::month, &createInputType<MonthInputType> },
125 { nullptr, &InputTypeNames::number, &createInputType<NumberInputType> },
126 { nullptr, &InputTypeNames::password, &createInputType<PasswordInputType> },
127 { nullptr, &InputTypeNames::radio, &createInputType<RadioInputType> },
128 { nullptr, &InputTypeNames::range, &createInputType<RangeInputType> },
129 { nullptr, &InputTypeNames::reset, &createInputType<ResetInputType> },
130 { nullptr, &InputTypeNames::search, &createInputType<SearchInputType> },
131 { nullptr, &InputTypeNames::submit, &createInputType<SubmitInputType> },
132 { nullptr, &InputTypeNames::telephone, &createInputType<TelephoneInputType> },
133 #if ENABLE(INPUT_TYPE_TIME)
134 { &Settings::inputTypeTimeEnabled, &InputTypeNames::time, &createInputType<TimeInputType> },
136 { nullptr, &InputTypeNames::url, &createInputType<URLInputType> },
137 #if ENABLE(INPUT_TYPE_WEEK)
138 { &Settings::inputTypeWeekEnabled, &InputTypeNames::week, &createInputType<WeekInputType> },
140 // No need to register "text" because it is the default type.
143 InputTypeFactoryMap map;
144 for (auto& inputType : inputTypes)
145 map.add(inputType.nameFunction(), std::make_pair(inputType.conditionalFunction, inputType.factoryFunction));
149 Ref<InputType> InputType::create(HTMLInputElement& element, const AtomString& typeName)
151 if (!typeName.isEmpty()) {
152 static NeverDestroyed factoryMap = createInputTypeFactoryMap();
153 auto&& [conditional, factory] = factoryMap.get().get(typeName.convertToASCIILowercase());
154 if (factory && (!conditional || std::invoke(conditional, element.document().settings())))
155 return factory(element);
157 return adoptRef(*new TextInputType(element));
160 Ref<InputType> InputType::createText(HTMLInputElement& element)
162 return adoptRef(*new TextInputType(element));
165 InputType::~InputType() = default;
167 bool InputType::themeSupportsDataListUI(InputType* type)
169 return RenderTheme::singleton().supportsDataListUI(type->formControlType());
172 template<typename T> static bool validateInputType(const T& inputType, const String& value)
174 ASSERT(inputType.canSetStringValue());
175 return !inputType.typeMismatchFor(value)
176 && !inputType.stepMismatch(value)
177 && !inputType.rangeUnderflow(value)
178 && !inputType.rangeOverflow(value)
179 && !inputType.patternMismatch(value)
180 && !inputType.valueMissing(value);
183 bool InputType::isValidValue(const String& value) const
187 return validateInputType(downcast<ButtonInputType>(*this), value);
189 return validateInputType(downcast<CheckboxInputType>(*this), value);
190 #if ENABLE(INPUT_TYPE_COLOR)
192 return validateInputType(downcast<ColorInputType>(*this), value);
194 #if ENABLE(INPUT_TYPE_DATE)
196 return validateInputType(downcast<DateInputType>(*this), value);
198 #if ENABLE(INPUT_TYPE_DATETIMELOCAL)
199 case Type::DateTimeLocal:
200 return validateInputType(downcast<DateTimeLocalInputType>(*this), value);
203 return validateInputType(downcast<EmailInputType>(*this), value);
205 return validateInputType(downcast<FileInputType>(*this), value);
207 return validateInputType(downcast<HiddenInputType>(*this), value);
209 return validateInputType(downcast<ImageInputType>(*this), value);
210 #if ENABLE(INPUT_TYPE_MONTH)
212 return validateInputType(downcast<MonthInputType>(*this), value);
215 return validateInputType(downcast<NumberInputType>(*this), value);
217 return validateInputType(downcast<PasswordInputType>(*this), value);
219 return validateInputType(downcast<RadioInputType>(*this), value);
221 return validateInputType(downcast<RangeInputType>(*this), value);
223 return validateInputType(downcast<ResetInputType>(*this), value);
225 return validateInputType(downcast<SearchInputType>(*this), value);
227 return validateInputType(downcast<SubmitInputType>(*this), value);
228 case Type::Telephone:
229 return validateInputType(downcast<TelephoneInputType>(*this), value);
230 #if ENABLE(INPUT_TYPE_TIME)
232 return validateInputType(downcast<TimeInputType>(*this), value);
235 return validateInputType(downcast<URLInputType>(*this), value);
236 #if ENABLE(INPUT_TYPE_WEEK)
238 return validateInputType(downcast<WeekInputType>(*this), value);
241 return validateInputType(downcast<TextInputType>(*this), value);
243 ASSERT_NOT_REACHED();
247 bool InputType::shouldSaveAndRestoreFormControlState() const
252 FormControlState InputType::saveFormControlState() const
255 auto currentValue = element()->value();
256 if (currentValue == element()->defaultValue())
258 return { { AtomString { currentValue } } };
261 void InputType::restoreFormControlState(const FormControlState& state)
264 element()->setValue(state[0]);
267 bool InputType::isFormDataAppendable() const
270 // There is no form data unless there's a name for non-image types.
271 return !element()->name().isEmpty();
274 bool InputType::appendFormData(DOMFormData& formData) const
277 // Always successful.
278 formData.append(element()->name(), element()->value());
282 WallTime InputType::valueAsDate() const
284 return WallTime::nan();
287 ExceptionOr<void> InputType::setValueAsDate(WallTime) const
289 return Exception { InvalidStateError };
292 double InputType::valueAsDouble() const
294 return std::numeric_limits<double>::quiet_NaN();
297 ExceptionOr<void> InputType::setValueAsDouble(double doubleValue, TextFieldEventBehavior eventBehavior) const
299 return setValueAsDecimal(Decimal::fromDouble(doubleValue), eventBehavior);
302 ExceptionOr<void> InputType::setValueAsDecimal(const Decimal&, TextFieldEventBehavior) const
304 return Exception { InvalidStateError };
307 bool InputType::typeMismatchFor(const String&) const
312 bool InputType::typeMismatch() const
317 bool InputType::supportsRequired() const
319 // Almost all validatable types support @required.
320 return supportsValidation();
323 bool InputType::valueMissing(const String&) const
328 bool InputType::hasBadInput() const
333 bool InputType::patternMismatch(const String&) const
338 bool InputType::rangeUnderflow(const String& value) const
343 const Decimal numericValue = parseToNumberOrNaN(value);
344 if (!numericValue.isFinite())
347 auto range = createStepRange(AnyStepHandling::Reject);
349 if (range.isReversible() && range.maximum() < range.minimum())
350 return numericValue > range.maximum() && numericValue < range.minimum();
352 return numericValue < range.minimum();
355 bool InputType::rangeOverflow(const String& value) const
360 const Decimal numericValue = parseToNumberOrNaN(value);
361 if (!numericValue.isFinite())
364 auto range = createStepRange(AnyStepHandling::Reject);
366 if (range.isReversible() && range.maximum() < range.minimum())
367 return numericValue > range.maximum() && numericValue < range.minimum();
369 return numericValue > range.maximum();
372 bool InputType::isInvalid(const String& value) const
376 return isInvalidInputType<ButtonInputType>(*this, value);
378 return isInvalidInputType<CheckboxInputType>(*this, value);
380 #if ENABLE(INPUT_TYPE_COLOR)
381 return isInvalidInputType<ColorInputType>(*this, value);
386 #if ENABLE(INPUT_TYPE_DATE)
387 return isInvalidInputType<DateInputType>(*this, value);
391 case Type::DateTimeLocal:
392 #if ENABLE(INPUT_TYPE_DATETIMELOCAL)
393 return isInvalidInputType<DateTimeLocalInputType>(*this, value);
398 return isInvalidInputType<EmailInputType>(*this, value);
400 return isInvalidInputType<FileInputType>(*this, value);
402 return isInvalidInputType<HiddenInputType>(*this, value);
404 return isInvalidInputType<ImageInputType>(*this, value);
406 #if ENABLE(INPUT_TYPE_MONTH)
407 return isInvalidInputType<MonthInputType>(*this, value);
412 return isInvalidInputType<NumberInputType>(*this, value);
414 return isInvalidInputType<PasswordInputType>(*this, value);
416 return isInvalidInputType<RadioInputType>(*this, value);
418 return isInvalidInputType<RangeInputType>(*this, value);
420 return isInvalidInputType<ResetInputType>(*this, value);
422 return isInvalidInputType<SearchInputType>(*this, value);
424 return isInvalidInputType<SubmitInputType>(*this, value);
425 case Type::Telephone:
426 return isInvalidInputType<TelephoneInputType>(*this, value);
428 #if ENABLE(INPUT_TYPE_TIME)
429 return isInvalidInputType<TimeInputType>(*this, value);
434 return isInvalidInputType<URLInputType>(*this, value);
436 #if ENABLE(INPUT_TYPE_WEEK)
437 return isInvalidInputType<WeekInputType>(*this, value);
442 return isInvalidInputType<TextInputType>(*this, value);
444 ASSERT_NOT_REACHED();
448 Decimal InputType::defaultValueForStepUp() const
453 double InputType::minimum() const
455 return createStepRange(AnyStepHandling::Reject).minimum().toDouble();
458 double InputType::maximum() const
460 return createStepRange(AnyStepHandling::Reject).maximum().toDouble();
463 bool InputType::sizeShouldIncludeDecoration(int, int& preferredSize) const
466 preferredSize = element()->size();
470 float InputType::decorationWidth() const
475 bool InputType::isInRange(const String& value) const
480 StepRange stepRange(createStepRange(AnyStepHandling::Reject));
481 if (!stepRange.hasRangeLimitations())
484 const Decimal numericValue = parseToNumberOrNaN(value);
485 if (!numericValue.isFinite())
488 return numericValue >= stepRange.minimum() && numericValue <= stepRange.maximum();
491 bool InputType::isOutOfRange(const String& value) const
493 if (!isSteppable() || value.isEmpty())
496 StepRange stepRange(createStepRange(AnyStepHandling::Reject));
497 if (!stepRange.hasRangeLimitations())
500 const Decimal numericValue = parseToNumberOrNaN(value);
501 if (!numericValue.isFinite())
504 return numericValue < stepRange.minimum() || numericValue > stepRange.maximum();
507 bool InputType::stepMismatch(const String& value) const
512 const Decimal numericValue = parseToNumberOrNaN(value);
513 if (!numericValue.isFinite())
516 return createStepRange(AnyStepHandling::Reject).stepMismatch(numericValue);
519 String InputType::badInputText() const
521 ASSERT_NOT_REACHED();
522 return validationMessageTypeMismatchText();
525 String InputType::typeMismatchText() const
527 return validationMessageTypeMismatchText();
530 String InputType::valueMissingText() const
532 return validationMessageValueMissingText();
535 String InputType::validationMessage() const
538 String value = element()->value();
540 // The order of the following checks is meaningful. e.g. We'd like to show the
541 // badInput message even if the control has other validation errors.
543 return badInputText();
545 if (valueMissing(value))
546 return valueMissingText();
549 return typeMismatchText();
551 if (patternMismatch(value))
552 return validationMessagePatternMismatchText();
554 if (element()->tooShort())
555 return validationMessageTooShortText(numGraphemeClusters(value), element()->minLength());
557 if (element()->tooLong())
558 return validationMessageTooLongText(numGraphemeClusters(value), element()->effectiveMaxLength());
561 return emptyString();
563 const Decimal numericValue = parseToNumberOrNaN(value);
564 if (!numericValue.isFinite())
565 return emptyString();
567 StepRange stepRange(createStepRange(AnyStepHandling::Reject));
569 if (numericValue < stepRange.minimum())
570 return validationMessageRangeUnderflowText(serialize(stepRange.minimum()));
572 if (numericValue > stepRange.maximum())
573 return validationMessageRangeOverflowText(serialize(stepRange.maximum()));
575 if (stepRange.stepMismatch(numericValue)) {
576 const String stepString = stepRange.hasStep() ? serializeForNumberType(stepRange.step() / stepRange.stepScaleFactor()) : emptyString();
577 return validationMessageStepMismatchText(serialize(stepRange.stepBase()), stepString);
580 return emptyString();
583 void InputType::handleClickEvent(MouseEvent&)
587 void InputType::handleMouseDownEvent(MouseEvent&)
591 void InputType::handleDOMActivateEvent(Event&)
595 bool InputType::allowsShowPickerAcrossFrames()
600 void InputType::showPicker()
604 auto InputType::handleKeydownEvent(KeyboardEvent&) -> ShouldCallBaseEventHandler
606 return ShouldCallBaseEventHandler::Yes;
609 void InputType::handleKeypressEvent(KeyboardEvent&)
613 void InputType::handleKeyupEvent(KeyboardEvent&)
617 void InputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent&)
621 #if ENABLE(TOUCH_EVENTS)
622 void InputType::handleTouchEvent(TouchEvent&)
627 void InputType::forwardEvent(Event&)
631 bool InputType::shouldSubmitImplicitly(Event& event)
633 return is<KeyboardEvent>(event) && event.type() == eventNames().keypressEvent && downcast<KeyboardEvent>(event).charCode() == '\r';
636 RenderPtr<RenderElement> InputType::createInputRenderer(RenderStyle&& style)
639 return RenderPtr<RenderElement>(RenderElement::createFor(*element(), WTFMove(style)));
642 void InputType::blur()
645 element()->defaultBlur();
648 void InputType::createShadowSubtree()
652 void InputType::destroyShadowSubtree()
655 RefPtr<ShadowRoot> root = element()->userAgentShadowRoot();
659 root->removeChildren();
662 Decimal InputType::parseToNumber(const String&, const Decimal& defaultValue) const
664 ASSERT_NOT_REACHED();
668 Decimal InputType::parseToNumberOrNaN(const String& string) const
670 return parseToNumber(string, Decimal::nan());
673 String InputType::serialize(const Decimal&) const
675 ASSERT_NOT_REACHED();
679 DateComponentsType InputType::dateType() const
681 return DateComponentsType::Invalid;
684 void InputType::dispatchSimulatedClickIfActive(KeyboardEvent& event) const
687 if (element()->active())
688 element()->dispatchSimulatedClick(&event);
689 event.setDefaultHandled();
692 Chrome* InputType::chrome() const
695 if (Page* page = element()->document().page())
696 return &page->chrome();
700 bool InputType::canSetStringValue() const
705 bool InputType::hasCustomFocusLogic() const
710 bool InputType::isKeyboardFocusable(KeyboardEvent* event) const
713 return !element()->isReadOnly() && element()->isTextFormControlKeyboardFocusable(event);
716 bool InputType::isMouseFocusable() const
719 return element()->isTextFormControlMouseFocusable();
722 bool InputType::shouldUseInputMethod() const
727 void InputType::handleFocusEvent(Node*, FocusDirection)
731 void InputType::handleBlurEvent()
735 bool InputType::accessKeyAction(bool)
738 element()->focus({ SelectionRestorationMode::SelectAll });
742 void InputType::addSearchResult()
746 void InputType::attach()
750 void InputType::detach()
754 bool InputType::shouldRespectAlignAttribute()
759 bool InputType::canBeSuccessfulSubmitButton()
764 HTMLElement* InputType::placeholderElement() const
769 bool InputType::rendererIsNeeded()
774 String InputType::fallbackValue() const
779 String InputType::defaultValue() const
784 bool InputType::shouldSendChangeEventAfterCheckedChanged()
789 bool InputType::storesValueSeparateFromAttribute()
794 void InputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior, TextControlSetValueSelection)
797 element()->setValueInternal(sanitizedValue, eventBehavior);
800 element()->invalidateStyleForSubtree();
802 switch (eventBehavior) {
803 case DispatchChangeEvent:
804 element()->dispatchFormControlChangeEvent();
806 case DispatchInputAndChangeEvent:
807 element()->dispatchFormControlInputEvent();
808 if (auto element = this->element())
809 element->dispatchFormControlChangeEvent();
811 case DispatchNoEvent:
815 if (isRangeControl()) {
816 if (auto* cache = element()->document().existingAXObjectCache())
817 cache->postNotification(element(), AXObjectCache::AXValueChanged);
821 void InputType::willDispatchClick(InputElementClickState&)
825 void InputType::didDispatchClick(Event&, const InputElementClickState&)
829 String InputType::localizeValue(const String& proposedValue) const
831 return proposedValue;
834 String InputType::visibleValue() const
837 return element()->value();
840 bool InputType::isEmptyValue() const
845 String InputType::sanitizeValue(const String& proposedValue) const
847 return proposedValue;
850 #if ENABLE(DRAG_SUPPORT)
852 bool InputType::receiveDroppedFiles(const DragData&)
854 ASSERT_NOT_REACHED();
860 Icon* InputType::icon() const
862 ASSERT_NOT_REACHED();
866 String InputType::displayString() const
868 ASSERT_NOT_REACHED();
872 bool InputType::shouldResetOnDocumentActivation()
877 bool InputType::shouldRespectListAttribute()
882 bool InputType::isInteractiveContent() const
884 return m_type != Type::Hidden;
887 bool InputType::supportLabels() const
889 return m_type != Type::Hidden;
892 bool InputType::isEnumeratable() const
894 return m_type != Type::Image;
897 bool InputType::shouldRespectHeightAndWidthAttributes()
902 bool InputType::supportsPlaceholder() const
907 bool InputType::supportsReadOnly() const
912 void InputType::updateInnerTextValue()
916 void InputType::updatePlaceholderText()
920 void InputType::capsLockStateMayHaveChanged()
924 void InputType::updateAutoFillButton()
928 void InputType::subtreeHasChanged()
930 ASSERT_NOT_REACHED();
933 #if ENABLE(TOUCH_EVENTS)
934 bool InputType::hasTouchEventHandler() const
940 String InputType::defaultToolTip() const
945 #if ENABLE(DATALIST_ELEMENT)
946 void InputType::dataListMayHaveChanged()
950 std::optional<Decimal> InputType::findClosestTickMarkValue(const Decimal&)
952 ASSERT_NOT_REACHED();
957 bool InputType::matchesIndeterminatePseudoClass() const
962 bool InputType::shouldAppearIndeterminate() const
967 bool InputType::isPresentingAttachedView() const
972 bool InputType::supportsSelectionAPI() const
977 unsigned InputType::height() const
982 unsigned InputType::width() const
987 ExceptionOr<void> InputType::applyStep(int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior)
989 // https://html.spec.whatwg.org/C/#dom-input-stepup
991 StepRange stepRange(createStepRange(anyStepHandling));
992 if (!stepRange.hasStep())
993 return Exception { InvalidStateError };
995 // 3. If the element has a minimum and a maximum and the minimum is greater than the maximum, then abort these steps.
996 if (stepRange.minimum() > stepRange.maximum())
999 // 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
1000 // the element's maximum that, when subtracted from the step base, is an integral multiple of the allowed value step, then abort these steps.
1001 Decimal alignedMaximum = stepRange.stepSnappedMaximum();
1002 if (!alignedMaximum.isFinite())
1006 const Decimal current = parseToNumber(element()->value(), 0);
1007 Decimal base = stepRange.stepBase();
1008 Decimal step = stepRange.step();
1009 Decimal newValue = current;
1011 newValue = newValue + stepRange.step() * Decimal::fromDouble(count);
1012 const AtomString& stepString = element()->getAttribute(HTMLNames::stepAttr);
1013 if (!equalLettersIgnoringASCIICase(stepString, "any"_s))
1014 newValue = stepRange.alignValueForStep(current, newValue);
1016 // 8. 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
1017 // base, is an integral multiple of the allowed value step, and that is more than or equal to minimum.
1018 if (newValue < stepRange.minimum()) {
1019 const Decimal alignedMinimum = base + ((stepRange.minimum() - base) / step).ceiling() * step;
1020 ASSERT(alignedMinimum >= stepRange.minimum());
1021 newValue = alignedMinimum;
1024 // 9. 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
1025 // base, is an integral multiple of the allowed value step, and that is less than or equal to maximum.
1026 if (newValue > stepRange.maximum())
1027 newValue = alignedMaximum;
1029 // 10. If either the method invoked was the stepDown() method and value is greater than valueBeforeStepping, or the method invoked was the stepUp()
1030 // method and value is less than valueBeforeStepping, then return.
1031 if ((count < 0 && current < newValue) || (count > 0 && current > newValue))
1034 Ref protectedThis { *this };
1035 auto result = setValueAsDecimal(newValue, eventBehavior);
1036 if (result.hasException() || !element())
1039 if (AXObjectCache* cache = element()->document().existingAXObjectCache())
1040 cache->postNotification(element(), AXObjectCache::AXValueChanged);
1045 bool InputType::getAllowedValueStep(Decimal* step) const
1047 StepRange stepRange(createStepRange(AnyStepHandling::Reject));
1048 *step = stepRange.step();
1049 return stepRange.hasStep();
1052 StepRange InputType::createStepRange(AnyStepHandling) const
1054 ASSERT_NOT_REACHED();
1058 ExceptionOr<void> InputType::stepUp(int n)
1061 return Exception { InvalidStateError };
1062 return applyStep(n, AnyStepHandling::Reject, DispatchNoEvent);
1065 void InputType::stepUpFromRenderer(int n)
1067 // The differences from stepUp()/stepDown():
1069 // Difference 1: the current value
1070 // If the current value is not a number, including empty, the current value is assumed as 0.
1071 // * If 0 is in-range, and matches to step value
1072 // - The value should be the +step if n > 0
1073 // - The value should be the -step if n < 0
1074 // If -step or +step is out of range, new value should be 0.
1075 // * If 0 is smaller than the minimum value
1076 // - The value should be the minimum value for any n
1077 // * If 0 is larger than the maximum value
1078 // - The value should be the maximum value for any n
1079 // * If 0 is in-range, but not matched to step value
1080 // - The value should be the larger matched value nearest to 0 if n > 0
1081 // e.g. <input type=number min=-100 step=3> -> 2
1082 // - The value should be the smaller matched value nearest to 0 if n < 0
1083 // e.g. <input type=number min=-100 step=3> -> -1
1084 // As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time".
1085 // As for datetime type, the current value is assumed as "the current date/time in UTC".
1086 // If the current value is smaller than the minimum value:
1087 // - The value should be the minimum value if n > 0
1088 // - Nothing should happen if n < 0
1089 // If the current value is larger than the maximum value:
1090 // - The value should be the maximum value if n < 0
1091 // - Nothing should happen if n > 0
1093 // Difference 2: clamping steps
1094 // If the current value is not matched to step value:
1095 // - The value should be the larger matched value nearest to 0 if n > 0
1096 // e.g. <input type=number value=3 min=-100 step=3> -> 5
1097 // - The value should be the smaller matched value nearest to 0 if n < 0
1098 // e.g. <input type=number value=3 min=-100 step=3> -> 2
1100 // n is assumed as -n if step < 0.
1102 ASSERT(isSteppable());
1109 StepRange stepRange(createStepRange(AnyStepHandling::Default));
1111 // FIXME: Not any changes after stepping, even if it is an invalid value, may be better.
1112 // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => "foo")
1113 if (!stepRange.hasStep())
1116 EventQueueScope scope;
1117 const Decimal step = stepRange.step();
1128 String currentStringValue = element()->value();
1129 Decimal current = parseToNumberOrNaN(currentStringValue);
1130 if (!current.isFinite()) {
1131 current = defaultValueForStepUp();
1132 const Decimal nextDiff = step * n;
1133 if (current < stepRange.minimum() - nextDiff)
1134 current = stepRange.minimum() - nextDiff;
1135 if (current > stepRange.maximum() - nextDiff)
1136 current = stepRange.maximum() - nextDiff;
1137 setValueAsDecimal(current, DispatchNoEvent);
1139 if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum()))
1140 setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchInputAndChangeEvent);
1142 if (stepMismatch(element()->value())) {
1143 ASSERT(!step.isZero());
1144 const Decimal base = stepRange.stepBase();
1147 newValue = base + ((current - base) / step).floor() * step;
1149 newValue = base + ((current - base) / step).ceiling() * step;
1153 if (newValue < stepRange.minimum())
1154 newValue = stepRange.minimum();
1155 if (newValue > stepRange.maximum())
1156 newValue = stepRange.maximum();
1158 setValueAsDecimal(newValue, n == 1 || n == -1 ? DispatchInputAndChangeEvent : DispatchNoEvent);
1160 applyStep(n - 1, AnyStepHandling::Default, DispatchInputAndChangeEvent);
1162 applyStep(n + 1, AnyStepHandling::Default, DispatchInputAndChangeEvent);
1164 applyStep(n, AnyStepHandling::Default, DispatchInputAndChangeEvent);
1168 RefPtr<TextControlInnerTextElement> InputType::innerTextElement() const
1173 RefPtr<TextControlInnerTextElement> InputType::innerTextElementCreatingShadowSubtreeIfNeeded()
1175 createShadowSubtreeIfNeeded();
1176 return innerTextElement();
1179 String InputType::resultForDialogSubmit() const
1182 return element()->value();
1185 void InputType::createShadowSubtreeIfNeeded()
1187 if (m_hasCreatedShadowSubtree || !needsShadowSubtree())
1189 Ref protectedThis { *this };
1190 element()->ensureUserAgentShadowRoot();
1191 m_hasCreatedShadowSubtree = true;
1192 createShadowSubtree();
1195 } // namespace WebCore