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 "PseudoClassChangeInvalidation.h"
63 #include "RadioInputType.h"
64 #include "RangeInputType.h"
65 #include "RenderElement.h"
66 #include "RenderTheme.h"
67 #include "ResetInputType.h"
68 #include "ScopedEventQueue.h"
69 #include "SearchInputType.h"
70 #include "SelectionRestorationMode.h"
72 #include "ShadowRoot.h"
73 #include "StepRange.h"
74 #include "SubmitInputType.h"
75 #include "TelephoneInputType.h"
76 #include "TextControlInnerElements.h"
77 #include "TextInputType.h"
78 #include "TimeInputType.h"
79 #include "URLInputType.h"
80 #include "WeekInputType.h"
82 #include <wtf/Assertions.h>
83 #include <wtf/RobinHoodHashMap.h>
84 #include <wtf/text/StringHash.h>
85 #include <wtf/text/TextBreakIterator.h>
89 using namespace HTMLNames;
91 typedef bool (Settings::*InputTypeConditionalFunction)() const;
92 typedef const AtomString& (*InputTypeNameFunction)();
93 typedef Ref<InputType> (*InputTypeFactoryFunction)(HTMLInputElement&);
94 typedef MemoryCompactLookupOnlyRobinHoodHashMap<AtomString, std::pair<InputTypeConditionalFunction, InputTypeFactoryFunction>> InputTypeFactoryMap;
96 template<class T> static Ref<InputType> createInputType(HTMLInputElement& element)
98 return adoptRef(*new T(element));
101 static InputTypeFactoryMap createInputTypeFactoryMap()
103 static const struct InputTypes {
104 InputTypeConditionalFunction conditionalFunction;
105 InputTypeNameFunction nameFunction;
106 InputTypeFactoryFunction factoryFunction;
108 { nullptr, &InputTypeNames::button, &createInputType<ButtonInputType> },
109 { nullptr, &InputTypeNames::checkbox, &createInputType<CheckboxInputType> },
110 #if ENABLE(INPUT_TYPE_COLOR)
111 { &Settings::inputTypeColorEnabled, &InputTypeNames::color, &createInputType<ColorInputType> },
113 #if ENABLE(INPUT_TYPE_DATE)
114 { &Settings::inputTypeDateEnabled, &InputTypeNames::date, &createInputType<DateInputType> },
116 #if ENABLE(INPUT_TYPE_DATETIMELOCAL)
117 { &Settings::inputTypeDateTimeLocalEnabled, &InputTypeNames::datetimelocal, &createInputType<DateTimeLocalInputType> },
119 { nullptr, &InputTypeNames::email, &createInputType<EmailInputType> },
120 { nullptr, &InputTypeNames::file, &createInputType<FileInputType> },
121 { nullptr, &InputTypeNames::hidden, &createInputType<HiddenInputType> },
122 { nullptr, &InputTypeNames::image, &createInputType<ImageInputType> },
123 #if ENABLE(INPUT_TYPE_MONTH)
124 { &Settings::inputTypeMonthEnabled, &InputTypeNames::month, &createInputType<MonthInputType> },
126 { nullptr, &InputTypeNames::number, &createInputType<NumberInputType> },
127 { nullptr, &InputTypeNames::password, &createInputType<PasswordInputType> },
128 { nullptr, &InputTypeNames::radio, &createInputType<RadioInputType> },
129 { nullptr, &InputTypeNames::range, &createInputType<RangeInputType> },
130 { nullptr, &InputTypeNames::reset, &createInputType<ResetInputType> },
131 { nullptr, &InputTypeNames::search, &createInputType<SearchInputType> },
132 { nullptr, &InputTypeNames::submit, &createInputType<SubmitInputType> },
133 { nullptr, &InputTypeNames::telephone, &createInputType<TelephoneInputType> },
134 { nullptr, &InputTypeNames::text, &createInputType<TextInputType> },
135 #if ENABLE(INPUT_TYPE_TIME)
136 { &Settings::inputTypeTimeEnabled, &InputTypeNames::time, &createInputType<TimeInputType> },
138 { nullptr, &InputTypeNames::url, &createInputType<URLInputType> },
139 #if ENABLE(INPUT_TYPE_WEEK)
140 { &Settings::inputTypeWeekEnabled, &InputTypeNames::week, &createInputType<WeekInputType> },
144 InputTypeFactoryMap map;
145 for (auto& inputType : inputTypes)
146 map.add(inputType.nameFunction(), std::make_pair(inputType.conditionalFunction, inputType.factoryFunction));
150 static inline std::pair<InputTypeConditionalFunction, InputTypeFactoryFunction> findFactory(const AtomString& typeName)
152 static NeverDestroyed factoryMap = createInputTypeFactoryMap();
153 auto factory = factoryMap.get().get(typeName);
154 if (UNLIKELY(!factory.second))
155 factory = factoryMap.get().get(typeName.convertToASCIILowercase());
159 Ref<InputType> InputType::create(HTMLInputElement& element, const AtomString& typeName)
161 if (!typeName.isEmpty()) {
162 auto [conditional, factory] = findFactory(typeName);
163 if (LIKELY(factory && (!conditional || std::invoke(conditional, element.document().settings()))))
164 return factory(element);
166 return adoptRef(*new TextInputType(element));
169 Ref<InputType> InputType::createText(HTMLInputElement& element)
171 return adoptRef(*new TextInputType(element));
174 InputType::~InputType() = default;
176 bool InputType::themeSupportsDataListUI(InputType* type)
178 return RenderTheme::singleton().supportsDataListUI(type->formControlType());
181 template<typename T> static bool validateInputType(const T& inputType, const String& value)
183 ASSERT(inputType.canSetStringValue());
184 return !inputType.typeMismatchFor(value)
185 && !inputType.stepMismatch(value)
186 && !inputType.rangeUnderflow(value)
187 && !inputType.rangeOverflow(value)
188 && !inputType.patternMismatch(value)
189 && !inputType.valueMissing(value);
192 bool InputType::isValidValue(const String& value) const
196 return validateInputType(downcast<ButtonInputType>(*this), value);
198 return validateInputType(downcast<CheckboxInputType>(*this), value);
199 #if ENABLE(INPUT_TYPE_COLOR)
201 return validateInputType(downcast<ColorInputType>(*this), value);
203 #if ENABLE(INPUT_TYPE_DATE)
205 return validateInputType(downcast<DateInputType>(*this), value);
207 #if ENABLE(INPUT_TYPE_DATETIMELOCAL)
208 case Type::DateTimeLocal:
209 return validateInputType(downcast<DateTimeLocalInputType>(*this), value);
212 return validateInputType(downcast<EmailInputType>(*this), value);
214 return validateInputType(downcast<FileInputType>(*this), value);
216 return validateInputType(downcast<HiddenInputType>(*this), value);
218 return validateInputType(downcast<ImageInputType>(*this), value);
219 #if ENABLE(INPUT_TYPE_MONTH)
221 return validateInputType(downcast<MonthInputType>(*this), value);
224 return validateInputType(downcast<NumberInputType>(*this), value);
226 return validateInputType(downcast<PasswordInputType>(*this), value);
228 return validateInputType(downcast<RadioInputType>(*this), value);
230 return validateInputType(downcast<RangeInputType>(*this), value);
232 return validateInputType(downcast<ResetInputType>(*this), value);
234 return validateInputType(downcast<SearchInputType>(*this), value);
236 return validateInputType(downcast<SubmitInputType>(*this), value);
237 case Type::Telephone:
238 return validateInputType(downcast<TelephoneInputType>(*this), value);
239 #if ENABLE(INPUT_TYPE_TIME)
241 return validateInputType(downcast<TimeInputType>(*this), value);
244 return validateInputType(downcast<URLInputType>(*this), value);
245 #if ENABLE(INPUT_TYPE_WEEK)
247 return validateInputType(downcast<WeekInputType>(*this), value);
250 return validateInputType(downcast<TextInputType>(*this), value);
252 ASSERT_NOT_REACHED();
256 bool InputType::shouldSaveAndRestoreFormControlState() const
261 FormControlState InputType::saveFormControlState() const
264 auto currentValue = element()->value();
265 if (currentValue == element()->defaultValue())
267 return { { AtomString { currentValue } } };
270 void InputType::restoreFormControlState(const FormControlState& state)
273 element()->setValue(state[0]);
276 bool InputType::isFormDataAppendable() const
279 // There is no form data unless there's a name for non-image types.
280 return !element()->name().isEmpty();
283 bool InputType::appendFormData(DOMFormData& formData) const
286 // Always successful.
287 formData.append(element()->name(), element()->value());
291 WallTime InputType::valueAsDate() const
293 return WallTime::nan();
296 ExceptionOr<void> InputType::setValueAsDate(WallTime) const
298 return Exception { InvalidStateError };
301 double InputType::valueAsDouble() const
303 return std::numeric_limits<double>::quiet_NaN();
306 ExceptionOr<void> InputType::setValueAsDouble(double doubleValue, TextFieldEventBehavior eventBehavior) const
308 return setValueAsDecimal(Decimal::fromDouble(doubleValue), eventBehavior);
311 ExceptionOr<void> InputType::setValueAsDecimal(const Decimal&, TextFieldEventBehavior) const
313 return Exception { InvalidStateError };
316 bool InputType::typeMismatchFor(const String&) const
321 bool InputType::typeMismatch() const
326 bool InputType::supportsRequired() const
328 // Almost all validatable types support @required.
329 return supportsValidation();
332 bool InputType::valueMissing(const String&) const
337 bool InputType::hasBadInput() const
342 bool InputType::patternMismatch(const String&) const
347 bool InputType::rangeUnderflow(const String& value) const
352 const Decimal numericValue = parseToNumberOrNaN(value);
353 if (!numericValue.isFinite())
356 auto range = createStepRange(AnyStepHandling::Reject);
358 if (range.isReversible() && range.maximum() < range.minimum())
359 return numericValue > range.maximum() && numericValue < range.minimum();
361 return numericValue < range.minimum();
364 bool InputType::rangeOverflow(const String& value) const
369 const Decimal numericValue = parseToNumberOrNaN(value);
370 if (!numericValue.isFinite())
373 auto range = createStepRange(AnyStepHandling::Reject);
375 if (range.isReversible() && range.maximum() < range.minimum())
376 return numericValue > range.maximum() && numericValue < range.minimum();
378 return numericValue > range.maximum();
381 bool InputType::isInvalid(const String& value) const
385 return isInvalidInputType<ButtonInputType>(*this, value);
387 return isInvalidInputType<CheckboxInputType>(*this, value);
389 #if ENABLE(INPUT_TYPE_COLOR)
390 return isInvalidInputType<ColorInputType>(*this, value);
395 #if ENABLE(INPUT_TYPE_DATE)
396 return isInvalidInputType<DateInputType>(*this, value);
400 case Type::DateTimeLocal:
401 #if ENABLE(INPUT_TYPE_DATETIMELOCAL)
402 return isInvalidInputType<DateTimeLocalInputType>(*this, value);
407 return isInvalidInputType<EmailInputType>(*this, value);
409 return isInvalidInputType<FileInputType>(*this, value);
411 return isInvalidInputType<HiddenInputType>(*this, value);
413 return isInvalidInputType<ImageInputType>(*this, value);
415 #if ENABLE(INPUT_TYPE_MONTH)
416 return isInvalidInputType<MonthInputType>(*this, value);
421 return isInvalidInputType<NumberInputType>(*this, value);
423 return isInvalidInputType<PasswordInputType>(*this, value);
425 return isInvalidInputType<RadioInputType>(*this, value);
427 return isInvalidInputType<RangeInputType>(*this, value);
429 return isInvalidInputType<ResetInputType>(*this, value);
431 return isInvalidInputType<SearchInputType>(*this, value);
433 return isInvalidInputType<SubmitInputType>(*this, value);
434 case Type::Telephone:
435 return isInvalidInputType<TelephoneInputType>(*this, value);
437 #if ENABLE(INPUT_TYPE_TIME)
438 return isInvalidInputType<TimeInputType>(*this, value);
443 return isInvalidInputType<URLInputType>(*this, value);
445 #if ENABLE(INPUT_TYPE_WEEK)
446 return isInvalidInputType<WeekInputType>(*this, value);
451 return isInvalidInputType<TextInputType>(*this, value);
453 ASSERT_NOT_REACHED();
457 Decimal InputType::defaultValueForStepUp() const
462 double InputType::minimum() const
464 return createStepRange(AnyStepHandling::Reject).minimum().toDouble();
467 double InputType::maximum() const
469 return createStepRange(AnyStepHandling::Reject).maximum().toDouble();
472 bool InputType::sizeShouldIncludeDecoration(int, int& preferredSize) const
475 preferredSize = element()->size();
479 float InputType::decorationWidth() const
484 bool InputType::isInRange(const String& value) const
489 StepRange stepRange(createStepRange(AnyStepHandling::Reject));
490 if (!stepRange.hasRangeLimitations())
493 const Decimal numericValue = parseToNumberOrNaN(value);
494 if (!numericValue.isFinite())
497 return numericValue >= stepRange.minimum() && numericValue <= stepRange.maximum();
500 bool InputType::isOutOfRange(const String& value) const
502 if (!isSteppable() || value.isEmpty())
505 StepRange stepRange(createStepRange(AnyStepHandling::Reject));
506 if (!stepRange.hasRangeLimitations())
509 const Decimal numericValue = parseToNumberOrNaN(value);
510 if (!numericValue.isFinite())
513 return numericValue < stepRange.minimum() || numericValue > stepRange.maximum();
516 bool InputType::stepMismatch(const String& value) const
521 const Decimal numericValue = parseToNumberOrNaN(value);
522 if (!numericValue.isFinite())
525 return createStepRange(AnyStepHandling::Reject).stepMismatch(numericValue);
528 String InputType::badInputText() const
530 ASSERT_NOT_REACHED();
531 return validationMessageTypeMismatchText();
534 String InputType::typeMismatchText() const
536 return validationMessageTypeMismatchText();
539 String InputType::valueMissingText() const
541 return validationMessageValueMissingText();
544 String InputType::validationMessage() const
547 String value = element()->value();
549 // The order of the following checks is meaningful. e.g. We'd like to show the
550 // badInput message even if the control has other validation errors.
552 return badInputText();
554 if (valueMissing(value))
555 return valueMissingText();
558 return typeMismatchText();
560 if (patternMismatch(value))
561 return validationMessagePatternMismatchText();
563 if (element()->tooShort())
564 return validationMessageTooShortText(numGraphemeClusters(value), element()->minLength());
566 if (element()->tooLong())
567 return validationMessageTooLongText(numGraphemeClusters(value), element()->effectiveMaxLength());
570 return emptyString();
572 const Decimal numericValue = parseToNumberOrNaN(value);
573 if (!numericValue.isFinite())
574 return emptyString();
576 StepRange stepRange(createStepRange(AnyStepHandling::Reject));
578 if (numericValue < stepRange.minimum())
579 return validationMessageRangeUnderflowText(serialize(stepRange.minimum()));
581 if (numericValue > stepRange.maximum())
582 return validationMessageRangeOverflowText(serialize(stepRange.maximum()));
584 if (stepRange.stepMismatch(numericValue)) {
585 const String stepString = stepRange.hasStep() ? serializeForNumberType(stepRange.step() / stepRange.stepScaleFactor()) : emptyString();
586 return validationMessageStepMismatchText(serialize(stepRange.stepBase()), stepString);
589 return emptyString();
592 void InputType::handleClickEvent(MouseEvent&)
596 void InputType::handleMouseDownEvent(MouseEvent&)
600 void InputType::handleDOMActivateEvent(Event&)
604 bool InputType::allowsShowPickerAcrossFrames()
609 void InputType::showPicker()
613 auto InputType::handleKeydownEvent(KeyboardEvent&) -> ShouldCallBaseEventHandler
615 return ShouldCallBaseEventHandler::Yes;
618 void InputType::handleKeypressEvent(KeyboardEvent&)
622 void InputType::handleKeyupEvent(KeyboardEvent&)
626 void InputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent&)
630 #if ENABLE(TOUCH_EVENTS)
631 void InputType::handleTouchEvent(TouchEvent&)
636 void InputType::forwardEvent(Event&)
640 bool InputType::shouldSubmitImplicitly(Event& event)
642 return is<KeyboardEvent>(event) && event.type() == eventNames().keypressEvent && downcast<KeyboardEvent>(event).charCode() == '\r';
645 RenderPtr<RenderElement> InputType::createInputRenderer(RenderStyle&& style)
648 return RenderPtr<RenderElement>(RenderElement::createFor(*element(), WTFMove(style)));
651 void InputType::blur()
654 element()->defaultBlur();
657 void InputType::createShadowSubtree()
661 void InputType::destroyShadowSubtree()
664 RefPtr<ShadowRoot> root = element()->userAgentShadowRoot();
668 root->removeChildren();
671 Decimal InputType::parseToNumber(const String&, const Decimal& defaultValue) const
673 ASSERT_NOT_REACHED();
677 Decimal InputType::parseToNumberOrNaN(const String& string) const
679 return parseToNumber(string, Decimal::nan());
682 String InputType::serialize(const Decimal&) const
684 ASSERT_NOT_REACHED();
688 DateComponentsType InputType::dateType() const
690 return DateComponentsType::Invalid;
693 void InputType::dispatchSimulatedClickIfActive(KeyboardEvent& event) const
696 if (element()->active())
697 element()->dispatchSimulatedClick(&event);
698 event.setDefaultHandled();
701 Chrome* InputType::chrome() const
704 if (Page* page = element()->document().page())
705 return &page->chrome();
709 bool InputType::canSetStringValue() const
714 bool InputType::hasCustomFocusLogic() const
719 bool InputType::isKeyboardFocusable(KeyboardEvent* event) const
722 return !element()->isReadOnly() && element()->isTextFormControlKeyboardFocusable(event);
725 bool InputType::isMouseFocusable() const
728 return element()->isTextFormControlMouseFocusable();
731 bool InputType::shouldUseInputMethod() const
736 void InputType::handleFocusEvent(Node*, FocusDirection)
740 void InputType::handleBlurEvent()
744 bool InputType::accessKeyAction(bool)
747 element()->focus({ SelectionRestorationMode::SelectAll });
751 void InputType::addSearchResult()
755 void InputType::attach()
759 void InputType::detach()
763 bool InputType::shouldRespectAlignAttribute()
768 bool InputType::canBeSuccessfulSubmitButton()
773 HTMLElement* InputType::placeholderElement() const
778 bool InputType::rendererIsNeeded()
783 String InputType::fallbackValue() const
788 String InputType::defaultValue() const
793 bool InputType::shouldSendChangeEventAfterCheckedChanged()
798 bool InputType::storesValueSeparateFromAttribute()
803 void InputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior, TextControlSetValueSelection)
807 element()->setValueInternal(sanitizedValue, eventBehavior);
811 bool wasInRange = isInRange(element()->value());
812 bool inRange = isInRange(sanitizedValue);
815 auto oldDirection = element()->directionalityIfhasDirAutoAttribute(dummy);
817 std::optional<Style::PseudoClassChangeInvalidation> styleInvalidation;
818 if (wasInRange != inRange)
819 emplace(styleInvalidation, *element(), { { CSSSelector::PseudoClassInRange, inRange }, { CSSSelector::PseudoClassOutOfRange, !inRange } });
821 element()->setValueInternal(sanitizedValue, eventBehavior);
823 if (oldDirection != element()->directionalityIfhasDirAutoAttribute(dummy))
824 element()->invalidateStyleInternal();
826 switch (eventBehavior) {
827 case DispatchChangeEvent:
828 element()->dispatchFormControlChangeEvent();
830 case DispatchInputAndChangeEvent:
831 element()->dispatchFormControlInputEvent();
832 if (auto element = this->element())
833 element->dispatchFormControlChangeEvent();
835 case DispatchNoEvent:
839 if (isRangeControl()) {
840 if (auto* cache = element()->document().existingAXObjectCache())
841 cache->postNotification(element(), AXObjectCache::AXValueChanged);
845 void InputType::willDispatchClick(InputElementClickState&)
849 void InputType::didDispatchClick(Event&, const InputElementClickState&)
853 String InputType::localizeValue(const String& proposedValue) const
855 return proposedValue;
858 String InputType::visibleValue() const
861 return element()->value();
864 bool InputType::isEmptyValue() const
869 String InputType::sanitizeValue(const String& proposedValue) const
871 return proposedValue;
874 #if ENABLE(DRAG_SUPPORT)
876 bool InputType::receiveDroppedFiles(const DragData&)
878 ASSERT_NOT_REACHED();
884 Icon* InputType::icon() const
886 ASSERT_NOT_REACHED();
890 String InputType::displayString() const
892 ASSERT_NOT_REACHED();
896 bool InputType::shouldResetOnDocumentActivation()
901 bool InputType::shouldRespectListAttribute()
906 bool InputType::isInteractiveContent() const
908 return m_type != Type::Hidden;
911 bool InputType::supportLabels() const
913 return m_type != Type::Hidden;
916 bool InputType::isEnumeratable() const
918 return m_type != Type::Image;
921 bool InputType::shouldRespectHeightAndWidthAttributes()
926 bool InputType::supportsPlaceholder() const
931 bool InputType::supportsReadOnly() const
936 void InputType::updateInnerTextValue()
940 void InputType::updatePlaceholderText()
944 void InputType::capsLockStateMayHaveChanged()
948 void InputType::updateAutoFillButton()
952 void InputType::subtreeHasChanged()
954 ASSERT_NOT_REACHED();
957 #if ENABLE(TOUCH_EVENTS)
958 bool InputType::hasTouchEventHandler() const
964 String InputType::defaultToolTip() const
969 #if ENABLE(DATALIST_ELEMENT)
970 void InputType::dataListMayHaveChanged()
974 std::optional<Decimal> InputType::findClosestTickMarkValue(const Decimal&)
976 ASSERT_NOT_REACHED();
981 bool InputType::matchesIndeterminatePseudoClass() const
986 bool InputType::shouldAppearIndeterminate() const
991 bool InputType::isPresentingAttachedView() const
996 bool InputType::supportsSelectionAPI() const
1001 unsigned InputType::height() const
1006 unsigned InputType::width() const
1011 ExceptionOr<void> InputType::applyStep(int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior)
1013 // https://html.spec.whatwg.org/C/#dom-input-stepup
1015 StepRange stepRange(createStepRange(anyStepHandling));
1016 if (!stepRange.hasStep())
1017 return Exception { InvalidStateError };
1019 // 3. If the element has a minimum and a maximum and the minimum is greater than the maximum, then abort these steps.
1020 if (stepRange.minimum() > stepRange.maximum())
1023 // 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
1024 // the element's maximum that, when subtracted from the step base, is an integral multiple of the allowed value step, then abort these steps.
1025 Decimal alignedMaximum = stepRange.stepSnappedMaximum();
1026 if (!alignedMaximum.isFinite())
1030 const Decimal current = parseToNumber(element()->value(), 0);
1031 Decimal base = stepRange.stepBase();
1032 Decimal step = stepRange.step();
1033 Decimal newValue = current;
1035 newValue = newValue + stepRange.step() * Decimal::fromDouble(count);
1036 const AtomString& stepString = element()->getAttribute(HTMLNames::stepAttr);
1037 if (!equalLettersIgnoringASCIICase(stepString, "any"_s))
1038 newValue = stepRange.alignValueForStep(current, newValue);
1040 // 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
1041 // base, is an integral multiple of the allowed value step, and that is more than or equal to minimum.
1042 if (newValue < stepRange.minimum()) {
1043 const Decimal alignedMinimum = base + ((stepRange.minimum() - base) / step).ceiling() * step;
1044 ASSERT(alignedMinimum >= stepRange.minimum());
1045 newValue = alignedMinimum;
1048 // 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
1049 // base, is an integral multiple of the allowed value step, and that is less than or equal to maximum.
1050 if (newValue > stepRange.maximum())
1051 newValue = alignedMaximum;
1053 // 10. If either the method invoked was the stepDown() method and value is greater than valueBeforeStepping, or the method invoked was the stepUp()
1054 // method and value is less than valueBeforeStepping, then return.
1055 if ((count < 0 && current < newValue) || (count > 0 && current > newValue))
1058 Ref protectedThis { *this };
1059 auto result = setValueAsDecimal(newValue, eventBehavior);
1060 if (result.hasException() || !element())
1063 if (AXObjectCache* cache = element()->document().existingAXObjectCache())
1064 cache->postNotification(element(), AXObjectCache::AXValueChanged);
1069 bool InputType::getAllowedValueStep(Decimal* step) const
1071 StepRange stepRange(createStepRange(AnyStepHandling::Reject));
1072 *step = stepRange.step();
1073 return stepRange.hasStep();
1076 StepRange InputType::createStepRange(AnyStepHandling) const
1078 ASSERT_NOT_REACHED();
1082 ExceptionOr<void> InputType::stepUp(int n)
1085 return Exception { InvalidStateError };
1086 return applyStep(n, AnyStepHandling::Reject, DispatchNoEvent);
1089 void InputType::stepUpFromRenderer(int n)
1091 // The differences from stepUp()/stepDown():
1093 // Difference 1: the current value
1094 // If the current value is not a number, including empty, the current value is assumed as 0.
1095 // * If 0 is in-range, and matches to step value
1096 // - The value should be the +step if n > 0
1097 // - The value should be the -step if n < 0
1098 // If -step or +step is out of range, new value should be 0.
1099 // * If 0 is smaller than the minimum value
1100 // - The value should be the minimum value for any n
1101 // * If 0 is larger than the maximum value
1102 // - The value should be the maximum value for any n
1103 // * If 0 is in-range, but not matched to step value
1104 // - The value should be the larger matched value nearest to 0 if n > 0
1105 // e.g. <input type=number min=-100 step=3> -> 2
1106 // - The value should be the smaller matched value nearest to 0 if n < 0
1107 // e.g. <input type=number min=-100 step=3> -> -1
1108 // As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time".
1109 // As for datetime type, the current value is assumed as "the current date/time in UTC".
1110 // If the current value is smaller than the minimum value:
1111 // - The value should be the minimum value if n > 0
1112 // - Nothing should happen if n < 0
1113 // If the current value is larger than the maximum value:
1114 // - The value should be the maximum value if n < 0
1115 // - Nothing should happen if n > 0
1117 // Difference 2: clamping steps
1118 // If the current value is not matched to step value:
1119 // - The value should be the larger matched value nearest to 0 if n > 0
1120 // e.g. <input type=number value=3 min=-100 step=3> -> 5
1121 // - The value should be the smaller matched value nearest to 0 if n < 0
1122 // e.g. <input type=number value=3 min=-100 step=3> -> 2
1124 // n is assumed as -n if step < 0.
1126 ASSERT(isSteppable());
1133 StepRange stepRange(createStepRange(AnyStepHandling::Default));
1135 // FIXME: Not any changes after stepping, even if it is an invalid value, may be better.
1136 // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => "foo")
1137 if (!stepRange.hasStep())
1140 EventQueueScope scope;
1141 const Decimal step = stepRange.step();
1152 String currentStringValue = element()->value();
1153 Decimal current = parseToNumberOrNaN(currentStringValue);
1154 if (!current.isFinite()) {
1155 current = defaultValueForStepUp();
1156 const Decimal nextDiff = step * n;
1157 if (current < stepRange.minimum() - nextDiff)
1158 current = stepRange.minimum() - nextDiff;
1159 if (current > stepRange.maximum() - nextDiff)
1160 current = stepRange.maximum() - nextDiff;
1161 setValueAsDecimal(current, DispatchNoEvent);
1163 if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum()))
1164 setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchInputAndChangeEvent);
1166 if (stepMismatch(element()->value())) {
1167 ASSERT(!step.isZero());
1168 const Decimal base = stepRange.stepBase();
1171 newValue = base + ((current - base) / step).floor() * step;
1173 newValue = base + ((current - base) / step).ceiling() * step;
1177 if (newValue < stepRange.minimum())
1178 newValue = stepRange.minimum();
1179 if (newValue > stepRange.maximum())
1180 newValue = stepRange.maximum();
1182 setValueAsDecimal(newValue, n == 1 || n == -1 ? DispatchInputAndChangeEvent : DispatchNoEvent);
1184 applyStep(n - 1, AnyStepHandling::Default, DispatchInputAndChangeEvent);
1186 applyStep(n + 1, AnyStepHandling::Default, DispatchInputAndChangeEvent);
1188 applyStep(n, AnyStepHandling::Default, DispatchInputAndChangeEvent);
1192 RefPtr<TextControlInnerTextElement> InputType::innerTextElement() const
1197 RefPtr<TextControlInnerTextElement> InputType::innerTextElementCreatingShadowSubtreeIfNeeded()
1199 createShadowSubtreeIfNeeded();
1200 return innerTextElement();
1203 String InputType::resultForDialogSubmit() const
1206 return element()->value();
1209 void InputType::createShadowSubtreeIfNeeded()
1211 if (m_hasCreatedShadowSubtree || !needsShadowSubtree())
1213 Ref protectedThis { *this };
1214 element()->ensureUserAgentShadowRoot();
1215 m_hasCreatedShadowSubtree = true;
1216 createShadowSubtree();
1219 } // namespace WebCore