1/*
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.
10 *
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.
15 *
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.
20 *
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.
25 *
26 */
27
28#include "config.h"
29#include "InputType.h"
30
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 "DateTimeInputType.h"
40#include "DateTimeLocalInputType.h"
41#include "EmailInputType.h"
42#include "EventNames.h"
43#include "FileInputType.h"
44#include "FileList.h"
45#include "FormController.h"
46#include "HTMLFormElement.h"
47#include "HTMLInputElement.h"
48#include "HTMLNames.h"
49#include "HTMLParserIdioms.h"
50#include "HiddenInputType.h"
51#include "ImageInputType.h"
52#include "InputTypeNames.h"
53#include "KeyboardEvent.h"
54#include "LocalizedStrings.h"
55#include "MonthInputType.h"
56#include "NodeRenderStyle.h"
57#include "NumberInputType.h"
58#include "Page.h"
59#include "PasswordInputType.h"
60#include "RadioInputType.h"
61#include "RangeInputType.h"
62#include "RenderElement.h"
63#include "RenderTheme.h"
64#include "ResetInputType.h"
65#include "RuntimeEnabledFeatures.h"
66#include "ScopedEventQueue.h"
67#include "SearchInputType.h"
68#include "ShadowRoot.h"
69#include "SubmitInputType.h"
70#include "TelephoneInputType.h"
71#include "TextControlInnerElements.h"
72#include "TextInputType.h"
73#include "TimeInputType.h"
74#include "URLInputType.h"
75#include "WeekInputType.h"
76#include <limits>
77#include <wtf/Assertions.h>
78#include <wtf/HashMap.h>
79#include <wtf/text/StringHash.h>
80#include <wtf/text/TextBreakIterator.h>
81
82namespace WebCore {
83
84using namespace HTMLNames;
85
86typedef bool (RuntimeEnabledFeatures::*InputTypeConditionalFunction)() const;
87typedef const AtomicString& (*InputTypeNameFunction)();
88typedef Ref<InputType> (*InputTypeFactoryFunction)(HTMLInputElement&);
89typedef HashMap<AtomicString, InputTypeFactoryFunction, ASCIICaseInsensitiveHash> InputTypeFactoryMap;
90
91template<class T>
92static Ref<InputType> createInputType(HTMLInputElement& element)
93{
94 return adoptRef(*new T(element));
95}
96
97static InputTypeFactoryMap createInputTypeFactoryMap()
98{
99 static const struct InputTypes {
100 InputTypeConditionalFunction conditionalFunction;
101 InputTypeNameFunction nameFunction;
102 InputTypeFactoryFunction factoryFunction;
103 } inputTypes[] = {
104 { nullptr, &InputTypeNames::button, &createInputType<ButtonInputType> },
105 { nullptr, &InputTypeNames::checkbox, &createInputType<CheckboxInputType> },
106#if ENABLE(INPUT_TYPE_COLOR)
107 { &RuntimeEnabledFeatures::inputTypeColorEnabled, &InputTypeNames::color, &createInputType<ColorInputType> },
108#endif
109#if ENABLE(INPUT_TYPE_DATE)
110 { &RuntimeEnabledFeatures::inputTypeDateEnabled, &InputTypeNames::date, &createInputType<DateInputType> },
111#endif
112#if ENABLE(INPUT_TYPE_DATETIME_INCOMPLETE)
113 { &RuntimeEnabledFeatures::inputTypeDateTimeEnabled, &InputTypeNames::datetime, &createInputType<DateTimeInputType> },
114#endif
115#if ENABLE(INPUT_TYPE_DATETIMELOCAL)
116 { &RuntimeEnabledFeatures::inputTypeDateTimeLocalEnabled, &InputTypeNames::datetimelocal, &createInputType<DateTimeLocalInputType> },
117#endif
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 { &RuntimeEnabledFeatures::inputTypeMonthEnabled, &InputTypeNames::month, &createInputType<MonthInputType> },
124#endif
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 { &RuntimeEnabledFeatures::inputTypeTimeEnabled, &InputTypeNames::time, &createInputType<TimeInputType> },
135#endif
136 { nullptr, &InputTypeNames::url, &createInputType<URLInputType> },
137#if ENABLE(INPUT_TYPE_WEEK)
138 { &RuntimeEnabledFeatures::inputTypeWeekEnabled, &InputTypeNames::week, &createInputType<WeekInputType> },
139#endif
140 // No need to register "text" because it is the default type.
141 };
142
143 InputTypeFactoryMap map;
144 for (auto& inputType : inputTypes) {
145 auto conditionalFunction = inputType.conditionalFunction;
146 if (!conditionalFunction || (RuntimeEnabledFeatures::sharedFeatures().*conditionalFunction)())
147 map.add(inputType.nameFunction(), inputType.factoryFunction);
148 }
149 return map;
150}
151
152Ref<InputType> InputType::create(HTMLInputElement& element, const AtomicString& typeName)
153{
154 if (!typeName.isEmpty()) {
155 static const auto factoryMap = makeNeverDestroyed(createInputTypeFactoryMap());
156 if (auto factory = factoryMap.get().get(typeName))
157 return factory(element);
158 }
159 return adoptRef(*new TextInputType(element));
160}
161
162Ref<InputType> InputType::createText(HTMLInputElement& element)
163{
164 return adoptRef(*new TextInputType(element));
165}
166
167InputType::~InputType() = default;
168
169bool InputType::themeSupportsDataListUI(InputType* type)
170{
171 return RenderTheme::singleton().supportsDataListUI(type->formControlType());
172}
173
174bool InputType::isTextField() const
175{
176 return false;
177}
178
179bool InputType::isTextType() const
180{
181 return false;
182}
183
184bool InputType::isRangeControl() const
185{
186 return false;
187}
188
189bool InputType::shouldSaveAndRestoreFormControlState() const
190{
191 return true;
192}
193
194FormControlState InputType::saveFormControlState() const
195{
196 ASSERT(element());
197 auto currentValue = element()->value();
198 if (currentValue == element()->defaultValue())
199 return { };
200 return { { currentValue } };
201}
202
203void InputType::restoreFormControlState(const FormControlState& state)
204{
205 ASSERT(element());
206 element()->setValue(state[0]);
207}
208
209bool InputType::isFormDataAppendable() const
210{
211 ASSERT(element());
212 // There is no form data unless there's a name for non-image types.
213 return !element()->name().isEmpty();
214}
215
216bool InputType::appendFormData(DOMFormData& formData, bool) const
217{
218 ASSERT(element());
219 // Always successful.
220 formData.append(element()->name(), element()->value());
221 return true;
222}
223
224double InputType::valueAsDate() const
225{
226 return DateComponents::invalidMilliseconds();
227}
228
229ExceptionOr<void> InputType::setValueAsDate(double) const
230{
231 return Exception { InvalidStateError };
232}
233
234double InputType::valueAsDouble() const
235{
236 return std::numeric_limits<double>::quiet_NaN();
237}
238
239ExceptionOr<void> InputType::setValueAsDouble(double doubleValue, TextFieldEventBehavior eventBehavior) const
240{
241 return setValueAsDecimal(Decimal::fromDouble(doubleValue), eventBehavior);
242}
243
244ExceptionOr<void> InputType::setValueAsDecimal(const Decimal&, TextFieldEventBehavior) const
245{
246 return Exception { InvalidStateError };
247}
248
249bool InputType::supportsValidation() const
250{
251 return true;
252}
253
254bool InputType::typeMismatchFor(const String&) const
255{
256 return false;
257}
258
259bool InputType::typeMismatch() const
260{
261 return false;
262}
263
264bool InputType::supportsRequired() const
265{
266 // Almost all validatable types support @required.
267 return supportsValidation();
268}
269
270bool InputType::valueMissing(const String&) const
271{
272 return false;
273}
274
275bool InputType::hasBadInput() const
276{
277 return false;
278}
279
280bool InputType::patternMismatch(const String&) const
281{
282 return false;
283}
284
285bool InputType::rangeUnderflow(const String& value) const
286{
287 if (!isSteppable())
288 return false;
289
290 const Decimal numericValue = parseToNumberOrNaN(value);
291 if (!numericValue.isFinite())
292 return false;
293
294 return numericValue < createStepRange(RejectAny).minimum();
295}
296
297bool InputType::rangeOverflow(const String& value) const
298{
299 if (!isSteppable())
300 return false;
301
302 const Decimal numericValue = parseToNumberOrNaN(value);
303 if (!numericValue.isFinite())
304 return false;
305
306 return numericValue > createStepRange(RejectAny).maximum();
307}
308
309Decimal InputType::defaultValueForStepUp() const
310{
311 return 0;
312}
313
314double InputType::minimum() const
315{
316 return createStepRange(RejectAny).minimum().toDouble();
317}
318
319double InputType::maximum() const
320{
321 return createStepRange(RejectAny).maximum().toDouble();
322}
323
324bool InputType::sizeShouldIncludeDecoration(int, int& preferredSize) const
325{
326 ASSERT(element());
327 preferredSize = element()->size();
328 return false;
329}
330
331float InputType::decorationWidth() const
332{
333 return 0;
334}
335
336bool InputType::isInRange(const String& value) const
337{
338 if (!isSteppable())
339 return false;
340
341 StepRange stepRange(createStepRange(RejectAny));
342 if (!stepRange.hasRangeLimitations())
343 return false;
344
345 const Decimal numericValue = parseToNumberOrNaN(value);
346 if (!numericValue.isFinite())
347 return true;
348
349 return numericValue >= stepRange.minimum() && numericValue <= stepRange.maximum();
350}
351
352bool InputType::isOutOfRange(const String& value) const
353{
354 if (!isSteppable() || value.isEmpty())
355 return false;
356
357 StepRange stepRange(createStepRange(RejectAny));
358 if (!stepRange.hasRangeLimitations())
359 return false;
360
361 const Decimal numericValue = parseToNumberOrNaN(value);
362 if (!numericValue.isFinite())
363 return true;
364
365 return numericValue < stepRange.minimum() || numericValue > stepRange.maximum();
366}
367
368bool InputType::stepMismatch(const String& value) const
369{
370 if (!isSteppable())
371 return false;
372
373 const Decimal numericValue = parseToNumberOrNaN(value);
374 if (!numericValue.isFinite())
375 return false;
376
377 return createStepRange(RejectAny).stepMismatch(numericValue);
378}
379
380String InputType::badInputText() const
381{
382 ASSERT_NOT_REACHED();
383 return validationMessageTypeMismatchText();
384}
385
386String InputType::typeMismatchText() const
387{
388 return validationMessageTypeMismatchText();
389}
390
391String InputType::valueMissingText() const
392{
393 return validationMessageValueMissingText();
394}
395
396String InputType::validationMessage() const
397{
398 ASSERT(element());
399 String value = element()->value();
400
401 // The order of the following checks is meaningful. e.g. We'd like to show the
402 // badInput message even if the control has other validation errors.
403 if (hasBadInput())
404 return badInputText();
405
406 if (valueMissing(value))
407 return valueMissingText();
408
409 if (typeMismatch())
410 return typeMismatchText();
411
412 if (patternMismatch(value))
413 return validationMessagePatternMismatchText();
414
415 if (element()->tooShort())
416 return validationMessageTooShortText(numGraphemeClusters(value), element()->minLength());
417
418 if (element()->tooLong())
419 return validationMessageTooLongText(numGraphemeClusters(value), element()->effectiveMaxLength());
420
421 if (!isSteppable())
422 return emptyString();
423
424 const Decimal numericValue = parseToNumberOrNaN(value);
425 if (!numericValue.isFinite())
426 return emptyString();
427
428 StepRange stepRange(createStepRange(RejectAny));
429
430 if (numericValue < stepRange.minimum())
431 return validationMessageRangeUnderflowText(serialize(stepRange.minimum()));
432
433 if (numericValue > stepRange.maximum())
434 return validationMessageRangeOverflowText(serialize(stepRange.maximum()));
435
436 if (stepRange.stepMismatch(numericValue)) {
437 const String stepString = stepRange.hasStep() ? serializeForNumberType(stepRange.step() / stepRange.stepScaleFactor()) : emptyString();
438 return validationMessageStepMismatchText(serialize(stepRange.stepBase()), stepString);
439 }
440
441 return emptyString();
442}
443
444void InputType::handleClickEvent(MouseEvent&)
445{
446}
447
448void InputType::handleMouseDownEvent(MouseEvent&)
449{
450}
451
452void InputType::handleDOMActivateEvent(Event&)
453{
454}
455
456auto InputType::handleKeydownEvent(KeyboardEvent&) -> ShouldCallBaseEventHandler
457{
458 return ShouldCallBaseEventHandler::Yes;
459}
460
461void InputType::handleKeypressEvent(KeyboardEvent&)
462{
463}
464
465void InputType::handleKeyupEvent(KeyboardEvent&)
466{
467}
468
469void InputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent&)
470{
471}
472
473#if ENABLE(TOUCH_EVENTS)
474void InputType::handleTouchEvent(TouchEvent&)
475{
476}
477#endif
478
479void InputType::forwardEvent(Event&)
480{
481}
482
483bool InputType::shouldSubmitImplicitly(Event& event)
484{
485 return is<KeyboardEvent>(event) && event.type() == eventNames().keypressEvent && downcast<KeyboardEvent>(event).charCode() == '\r';
486}
487
488RenderPtr<RenderElement> InputType::createInputRenderer(RenderStyle&& style)
489{
490 ASSERT(element());
491 return RenderPtr<RenderElement>(RenderElement::createFor(*element(), WTFMove(style)));
492}
493
494void InputType::blur()
495{
496 ASSERT(element());
497 element()->defaultBlur();
498}
499
500void InputType::createShadowSubtree()
501{
502}
503
504void InputType::destroyShadowSubtree()
505{
506 ASSERT(element());
507 RefPtr<ShadowRoot> root = element()->userAgentShadowRoot();
508 if (!root)
509 return;
510
511 root->removeChildren();
512}
513
514Decimal InputType::parseToNumber(const String&, const Decimal& defaultValue) const
515{
516 ASSERT_NOT_REACHED();
517 return defaultValue;
518}
519
520Decimal InputType::parseToNumberOrNaN(const String& string) const
521{
522 return parseToNumber(string, Decimal::nan());
523}
524
525bool InputType::parseToDateComponents(const String&, DateComponents*) const
526{
527 ASSERT_NOT_REACHED();
528 return false;
529}
530
531String InputType::serialize(const Decimal&) const
532{
533 ASSERT_NOT_REACHED();
534 return String();
535}
536
537#if PLATFORM(IOS_FAMILY)
538DateComponents::Type InputType::dateType() const
539{
540 return DateComponents::Invalid;
541}
542#endif
543
544void InputType::dispatchSimulatedClickIfActive(KeyboardEvent& event) const
545{
546 ASSERT(element());
547 if (element()->active())
548 element()->dispatchSimulatedClick(&event);
549 event.setDefaultHandled();
550}
551
552Chrome* InputType::chrome() const
553{
554 ASSERT(element());
555 if (Page* page = element()->document().page())
556 return &page->chrome();
557 return nullptr;
558}
559
560bool InputType::canSetStringValue() const
561{
562 return true;
563}
564
565bool InputType::hasCustomFocusLogic() const
566{
567 return true;
568}
569
570bool InputType::isKeyboardFocusable(KeyboardEvent* event) const
571{
572 ASSERT(element());
573 return !element()->isReadOnly() && element()->isTextFormControlKeyboardFocusable(event);
574}
575
576bool InputType::isMouseFocusable() const
577{
578 ASSERT(element());
579 return element()->isTextFormControlMouseFocusable();
580}
581
582bool InputType::shouldUseInputMethod() const
583{
584 return false;
585}
586
587void InputType::handleFocusEvent(Node*, FocusDirection)
588{
589}
590
591void InputType::handleBlurEvent()
592{
593}
594
595void InputType::accessKeyAction(bool)
596{
597 ASSERT(element());
598 element()->focus(false);
599}
600
601void InputType::addSearchResult()
602{
603}
604
605void InputType::attach()
606{
607}
608
609void InputType::detach()
610{
611}
612
613bool InputType::shouldRespectAlignAttribute()
614{
615 return false;
616}
617
618bool InputType::canBeSuccessfulSubmitButton()
619{
620 return false;
621}
622
623HTMLElement* InputType::placeholderElement() const
624{
625 return nullptr;
626}
627
628bool InputType::rendererIsNeeded()
629{
630 return true;
631}
632
633FileList* InputType::files()
634{
635 return nullptr;
636}
637
638void InputType::setFiles(RefPtr<FileList>&&)
639{
640}
641
642bool InputType::getTypeSpecificValue(String&)
643{
644 return false;
645}
646
647String InputType::fallbackValue() const
648{
649 return String();
650}
651
652String InputType::defaultValue() const
653{
654 return String();
655}
656
657bool InputType::shouldSendChangeEventAfterCheckedChanged()
658{
659 return true;
660}
661
662bool InputType::storesValueSeparateFromAttribute()
663{
664 return true;
665}
666
667void InputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
668{
669 ASSERT(element());
670 element()->setValueInternal(sanitizedValue, eventBehavior);
671 element()->invalidateStyleForSubtree();
672 if (!valueChanged)
673 return;
674
675 switch (eventBehavior) {
676 case DispatchChangeEvent:
677 element()->dispatchFormControlChangeEvent();
678 break;
679 case DispatchInputAndChangeEvent:
680 element()->dispatchFormControlInputEvent();
681 if (auto element = this->element())
682 element->dispatchFormControlChangeEvent();
683 break;
684 case DispatchNoEvent:
685 break;
686 }
687}
688
689bool InputType::canSetValue(const String&)
690{
691 return true;
692}
693
694void InputType::willDispatchClick(InputElementClickState&)
695{
696}
697
698void InputType::didDispatchClick(Event&, const InputElementClickState&)
699{
700}
701
702String InputType::localizeValue(const String& proposedValue) const
703{
704 return proposedValue;
705}
706
707String InputType::visibleValue() const
708{
709 ASSERT(element());
710 return element()->value();
711}
712
713bool InputType::isEmptyValue() const
714{
715 return true;
716}
717
718String InputType::sanitizeValue(const String& proposedValue) const
719{
720 return proposedValue;
721}
722
723#if ENABLE(DRAG_SUPPORT)
724
725bool InputType::receiveDroppedFiles(const DragData&)
726{
727 ASSERT_NOT_REACHED();
728 return false;
729}
730
731#endif
732
733Icon* InputType::icon() const
734{
735 ASSERT_NOT_REACHED();
736 return nullptr;
737}
738
739String InputType::displayString() const
740{
741 ASSERT_NOT_REACHED();
742 return String();
743}
744
745bool InputType::shouldResetOnDocumentActivation()
746{
747 return false;
748}
749
750bool InputType::shouldRespectListAttribute()
751{
752 return false;
753}
754
755bool InputType::isTextButton() const
756{
757 return false;
758}
759
760bool InputType::isRadioButton() const
761{
762 return false;
763}
764
765bool InputType::isSearchField() const
766{
767 return false;
768}
769
770bool InputType::isHiddenType() const
771{
772 return false;
773}
774
775bool InputType::isPasswordField() const
776{
777 return false;
778}
779
780bool InputType::isCheckbox() const
781{
782 return false;
783}
784
785bool InputType::isEmailField() const
786{
787 return false;
788}
789
790bool InputType::isFileUpload() const
791{
792 return false;
793}
794
795bool InputType::isImageButton() const
796{
797 return false;
798}
799
800bool InputType::supportLabels() const
801{
802 return true;
803}
804
805bool InputType::isNumberField() const
806{
807 return false;
808}
809
810bool InputType::isSubmitButton() const
811{
812 return false;
813}
814
815bool InputType::isTelephoneField() const
816{
817 return false;
818}
819
820bool InputType::isURLField() const
821{
822 return false;
823}
824
825bool InputType::isDateField() const
826{
827 return false;
828}
829
830bool InputType::isDateTimeField() const
831{
832 return false;
833}
834
835bool InputType::isDateTimeLocalField() const
836{
837 return false;
838}
839
840bool InputType::isMonthField() const
841{
842 return false;
843}
844
845bool InputType::isTimeField() const
846{
847 return false;
848}
849
850bool InputType::isWeekField() const
851{
852 return false;
853}
854
855bool InputType::isEnumeratable()
856{
857 return true;
858}
859
860bool InputType::isCheckable()
861{
862 return false;
863}
864
865bool InputType::isSteppable() const
866{
867 return false;
868}
869
870bool InputType::isColorControl() const
871{
872 return false;
873}
874
875bool InputType::shouldRespectHeightAndWidthAttributes()
876{
877 return false;
878}
879
880bool InputType::supportsPlaceholder() const
881{
882 return false;
883}
884
885bool InputType::supportsReadOnly() const
886{
887 return false;
888}
889
890void InputType::updateInnerTextValue()
891{
892}
893
894void InputType::updatePlaceholderText()
895{
896}
897
898void InputType::capsLockStateMayHaveChanged()
899{
900}
901
902void InputType::updateAutoFillButton()
903{
904}
905
906void InputType::subtreeHasChanged()
907{
908 ASSERT_NOT_REACHED();
909}
910
911#if ENABLE(TOUCH_EVENTS)
912bool InputType::hasTouchEventHandler() const
913{
914 return false;
915}
916#endif
917
918String InputType::defaultToolTip() const
919{
920 return String();
921}
922
923#if ENABLE(DATALIST_ELEMENT)
924void InputType::listAttributeTargetChanged()
925{
926}
927
928Optional<Decimal> InputType::findClosestTickMarkValue(const Decimal&)
929{
930 ASSERT_NOT_REACHED();
931 return WTF::nullopt;
932}
933#endif
934
935bool InputType::matchesIndeterminatePseudoClass() const
936{
937 return false;
938}
939
940bool InputType::shouldAppearIndeterminate() const
941{
942 return false;
943}
944
945bool InputType::isPresentingAttachedView() const
946{
947 return false;
948}
949
950bool InputType::supportsSelectionAPI() const
951{
952 return false;
953}
954
955unsigned InputType::height() const
956{
957 return 0;
958}
959
960unsigned InputType::width() const
961{
962 return 0;
963}
964
965ExceptionOr<void> InputType::applyStep(int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior)
966{
967 StepRange stepRange(createStepRange(anyStepHandling));
968 if (!stepRange.hasStep())
969 return Exception { InvalidStateError };
970
971 ASSERT(element());
972 const Decimal current = parseToNumberOrNaN(element()->value());
973 if (!current.isFinite())
974 return Exception { InvalidStateError };
975 Decimal newValue = current + stepRange.step() * count;
976 if (!newValue.isFinite())
977 return Exception { InvalidStateError };
978
979 const Decimal acceptableErrorValue = stepRange.acceptableError();
980 if (newValue - stepRange.minimum() < -acceptableErrorValue)
981 return Exception { InvalidStateError };
982 if (newValue < stepRange.minimum())
983 newValue = stepRange.minimum();
984
985 if (!equalLettersIgnoringASCIICase(element()->attributeWithoutSynchronization(stepAttr), "any"))
986 newValue = stepRange.alignValueForStep(current, newValue);
987
988 if (newValue - stepRange.maximum() > acceptableErrorValue)
989 return Exception { InvalidStateError };
990 if (newValue > stepRange.maximum())
991 newValue = stepRange.maximum();
992
993 auto result = setValueAsDecimal(newValue, eventBehavior);
994 if (result.hasException())
995 return result;
996
997 if (AXObjectCache* cache = element()->document().existingAXObjectCache())
998 cache->postNotification(element(), AXObjectCache::AXValueChanged);
999
1000 return result;
1001}
1002
1003bool InputType::getAllowedValueStep(Decimal* step) const
1004{
1005 StepRange stepRange(createStepRange(RejectAny));
1006 *step = stepRange.step();
1007 return stepRange.hasStep();
1008}
1009
1010StepRange InputType::createStepRange(AnyStepHandling) const
1011{
1012 ASSERT_NOT_REACHED();
1013 return StepRange();
1014}
1015
1016ExceptionOr<void> InputType::stepUp(int n)
1017{
1018 if (!isSteppable())
1019 return Exception { InvalidStateError };
1020 return applyStep(n, RejectAny, DispatchNoEvent);
1021}
1022
1023void InputType::stepUpFromRenderer(int n)
1024{
1025 // The differences from stepUp()/stepDown():
1026 //
1027 // Difference 1: the current value
1028 // If the current value is not a number, including empty, the current value is assumed as 0.
1029 // * If 0 is in-range, and matches to step value
1030 // - The value should be the +step if n > 0
1031 // - The value should be the -step if n < 0
1032 // If -step or +step is out of range, new value should be 0.
1033 // * If 0 is smaller than the minimum value
1034 // - The value should be the minimum value for any n
1035 // * If 0 is larger than the maximum value
1036 // - The value should be the maximum value for any n
1037 // * If 0 is in-range, but not matched to step value
1038 // - The value should be the larger matched value nearest to 0 if n > 0
1039 // e.g. <input type=number min=-100 step=3> -> 2
1040 // - The value should be the smaller matched value nearest to 0 if n < 0
1041 // e.g. <input type=number min=-100 step=3> -> -1
1042 // As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time".
1043 // As for datetime type, the current value is assumed as "the current date/time in UTC".
1044 // If the current value is smaller than the minimum value:
1045 // - The value should be the minimum value if n > 0
1046 // - Nothing should happen if n < 0
1047 // If the current value is larger than the maximum value:
1048 // - The value should be the maximum value if n < 0
1049 // - Nothing should happen if n > 0
1050 //
1051 // Difference 2: clamping steps
1052 // If the current value is not matched to step value:
1053 // - The value should be the larger matched value nearest to 0 if n > 0
1054 // e.g. <input type=number value=3 min=-100 step=3> -> 5
1055 // - The value should be the smaller matched value nearest to 0 if n < 0
1056 // e.g. <input type=number value=3 min=-100 step=3> -> 2
1057 //
1058 // n is assumed as -n if step < 0.
1059
1060 ASSERT(isSteppable());
1061 if (!isSteppable())
1062 return;
1063 ASSERT(n);
1064 if (!n)
1065 return;
1066
1067 StepRange stepRange(createStepRange(AnyIsDefaultStep));
1068
1069 // FIXME: Not any changes after stepping, even if it is an invalid value, may be better.
1070 // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => "foo")
1071 if (!stepRange.hasStep())
1072 return;
1073
1074 EventQueueScope scope;
1075 const Decimal step = stepRange.step();
1076
1077 int sign;
1078 if (step > 0)
1079 sign = n;
1080 else if (step < 0)
1081 sign = -n;
1082 else
1083 sign = 0;
1084
1085 ASSERT(element());
1086 String currentStringValue = element()->value();
1087 Decimal current = parseToNumberOrNaN(currentStringValue);
1088 if (!current.isFinite()) {
1089 current = defaultValueForStepUp();
1090 const Decimal nextDiff = step * n;
1091 if (current < stepRange.minimum() - nextDiff)
1092 current = stepRange.minimum() - nextDiff;
1093 if (current > stepRange.maximum() - nextDiff)
1094 current = stepRange.maximum() - nextDiff;
1095 setValueAsDecimal(current, DispatchNoEvent);
1096 }
1097 if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum()))
1098 setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchInputAndChangeEvent);
1099 else {
1100 if (stepMismatch(element()->value())) {
1101 ASSERT(!step.isZero());
1102 const Decimal base = stepRange.stepBase();
1103 Decimal newValue;
1104 if (sign < 0)
1105 newValue = base + ((current - base) / step).floor() * step;
1106 else if (sign > 0)
1107 newValue = base + ((current - base) / step).ceiling() * step;
1108 else
1109 newValue = current;
1110
1111 if (newValue < stepRange.minimum())
1112 newValue = stepRange.minimum();
1113 if (newValue > stepRange.maximum())
1114 newValue = stepRange.maximum();
1115
1116 setValueAsDecimal(newValue, n == 1 || n == -1 ? DispatchInputAndChangeEvent : DispatchNoEvent);
1117 if (n > 1)
1118 applyStep(n - 1, AnyIsDefaultStep, DispatchInputAndChangeEvent);
1119 else if (n < -1)
1120 applyStep(n + 1, AnyIsDefaultStep, DispatchInputAndChangeEvent);
1121 } else
1122 applyStep(n, AnyIsDefaultStep, DispatchInputAndChangeEvent);
1123 }
1124}
1125
1126Color InputType::valueAsColor() const
1127{
1128 return Color::transparent;
1129}
1130
1131void InputType::selectColor(StringView)
1132{
1133}
1134
1135Vector<Color> InputType::suggestedColors() const
1136{
1137 return { };
1138}
1139
1140RefPtr<TextControlInnerTextElement> InputType::innerTextElement() const
1141{
1142 return nullptr;
1143}
1144
1145} // namespace WebCore
1146