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, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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 "core/html/forms/InputType.h"
30
31#include "bindings/core/v8/ExceptionMessages.h"
32#include "bindings/core/v8/ExceptionState.h"
33#include "core/InputTypeNames.h"
34#include "core/accessibility/AXObjectCache.h"
35#include "core/dom/NodeRenderStyle.h"
36#include "core/events/KeyboardEvent.h"
37#include "core/events/ScopedEventQueue.h"
38#include "core/fileapi/FileList.h"
39#include "core/frame/FrameHost.h"
40#include "core/html/FormDataList.h"
41#include "core/html/HTMLInputElement.h"
42#include "core/html/HTMLShadowElement.h"
43#include "core/html/forms/ButtonInputType.h"
44#include "core/html/forms/CheckboxInputType.h"
45#include "core/html/forms/ColorChooser.h"
46#include "core/html/forms/ColorInputType.h"
47#include "core/html/forms/DateInputType.h"
48#include "core/html/forms/DateTimeLocalInputType.h"
49#include "core/html/forms/EmailInputType.h"
50#include "core/html/forms/FileInputType.h"
51#include "core/html/forms/FormController.h"
52#include "core/html/forms/HiddenInputType.h"
53#include "core/html/forms/ImageInputType.h"
54#include "core/html/forms/MonthInputType.h"
55#include "core/html/forms/NumberInputType.h"
56#include "core/html/forms/PasswordInputType.h"
57#include "core/html/forms/RadioInputType.h"
58#include "core/html/forms/RangeInputType.h"
59#include "core/html/forms/ResetInputType.h"
60#include "core/html/forms/SearchInputType.h"
61#include "core/html/forms/SubmitInputType.h"
62#include "core/html/forms/TelephoneInputType.h"
63#include "core/html/forms/TextInputType.h"
64#include "core/html/forms/TimeInputType.h"
65#include "core/html/forms/URLInputType.h"
66#include "core/html/forms/WeekInputType.h"
67#include "core/html/parser/HTMLParserIdioms.h"
68#include "core/rendering/RenderTheme.h"
69#include "platform/RuntimeEnabledFeatures.h"
70#include "platform/text/PlatformLocale.h"
71#include "platform/text/TextBreakIterator.h"
72
73namespace blink {
74
75using blink::WebLocalizedString;
76using namespace HTMLNames;
77
78typedef PassRefPtrWillBeRawPtr<InputType> (*InputTypeFactoryFunction)(HTMLInputElement&);
79typedef HashMap<AtomicString, InputTypeFactoryFunction, CaseFoldingHash> InputTypeFactoryMap;
80
81static PassOwnPtr<InputTypeFactoryMap> createInputTypeFactoryMap()
82{
83    OwnPtr<InputTypeFactoryMap> map = adoptPtr(new InputTypeFactoryMap);
84    map->add(InputTypeNames::button, ButtonInputType::create);
85    map->add(InputTypeNames::checkbox, CheckboxInputType::create);
86    map->add(InputTypeNames::color, ColorInputType::create);
87    map->add(InputTypeNames::date, DateInputType::create);
88    map->add(InputTypeNames::datetime_local, DateTimeLocalInputType::create);
89    map->add(InputTypeNames::email, EmailInputType::create);
90    map->add(InputTypeNames::file, FileInputType::create);
91    map->add(InputTypeNames::hidden, HiddenInputType::create);
92    map->add(InputTypeNames::image, ImageInputType::create);
93    map->add(InputTypeNames::month, MonthInputType::create);
94    map->add(InputTypeNames::number, NumberInputType::create);
95    map->add(InputTypeNames::password, PasswordInputType::create);
96    map->add(InputTypeNames::radio, RadioInputType::create);
97    map->add(InputTypeNames::range, RangeInputType::create);
98    map->add(InputTypeNames::reset, ResetInputType::create);
99    map->add(InputTypeNames::search, SearchInputType::create);
100    map->add(InputTypeNames::submit, SubmitInputType::create);
101    map->add(InputTypeNames::tel, TelephoneInputType::create);
102    map->add(InputTypeNames::time, TimeInputType::create);
103    map->add(InputTypeNames::url, URLInputType::create);
104    map->add(InputTypeNames::week, WeekInputType::create);
105    // No need to register "text" because it is the default type.
106    return map.release();
107}
108
109static const InputTypeFactoryMap* factoryMap()
110{
111    static const InputTypeFactoryMap* factoryMap = createInputTypeFactoryMap().leakPtr();
112    return factoryMap;
113}
114
115PassRefPtrWillBeRawPtr<InputType> InputType::create(HTMLInputElement& element, const AtomicString& typeName)
116{
117    InputTypeFactoryFunction factory = typeName.isEmpty() ? 0 : factoryMap()->get(typeName);
118    if (!factory)
119        factory = TextInputType::create;
120    return factory(element);
121}
122
123PassRefPtrWillBeRawPtr<InputType> InputType::createText(HTMLInputElement& element)
124{
125    return TextInputType::create(element);
126}
127
128const AtomicString& InputType::normalizeTypeName(const AtomicString& typeName)
129{
130    if (typeName.isEmpty())
131        return InputTypeNames::text;
132    InputTypeFactoryMap::const_iterator it = factoryMap()->find(typeName);
133    return it == factoryMap()->end() ? InputTypeNames::text : it->key;
134}
135
136InputType::~InputType()
137{
138}
139
140bool InputType::isTextField() const
141{
142    return false;
143}
144
145bool InputType::shouldSaveAndRestoreFormControlState() const
146{
147    return true;
148}
149
150FormControlState InputType::saveFormControlState() const
151{
152    String currentValue = element().value();
153    if (currentValue == element().defaultValue())
154        return FormControlState();
155    return FormControlState(currentValue);
156}
157
158void InputType::restoreFormControlState(const FormControlState& state)
159{
160    element().setValue(state[0]);
161}
162
163bool InputType::isFormDataAppendable() const
164{
165    // There is no form data unless there's a name for non-image types.
166    return !element().name().isEmpty();
167}
168
169bool InputType::appendFormData(FormDataList& encoding, bool) const
170{
171    // Always successful.
172    encoding.appendData(element().name(), element().value());
173    return true;
174}
175
176String InputType::resultForDialogSubmit() const
177{
178    return element().fastGetAttribute(valueAttr);
179}
180
181double InputType::valueAsDate() const
182{
183    return DateComponents::invalidMilliseconds();
184}
185
186void InputType::setValueAsDate(double, ExceptionState& exceptionState) const
187{
188    exceptionState.throwDOMException(InvalidStateError, "This input element does not support Date values.");
189}
190
191double InputType::valueAsDouble() const
192{
193    return std::numeric_limits<double>::quiet_NaN();
194}
195
196void InputType::setValueAsDouble(double doubleValue, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState) const
197{
198    exceptionState.throwDOMException(InvalidStateError, "This input element does not support Number values.");
199}
200
201void InputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior, ExceptionState&) const
202{
203    element().setValue(serialize(newValue), eventBehavior);
204}
205
206bool InputType::supportsValidation() const
207{
208    return true;
209}
210
211bool InputType::typeMismatchFor(const String&) const
212{
213    return false;
214}
215
216bool InputType::typeMismatch() const
217{
218    return false;
219}
220
221bool InputType::supportsRequired() const
222{
223    // Almost all validatable types support @required.
224    return supportsValidation();
225}
226
227bool InputType::valueMissing(const String&) const
228{
229    return false;
230}
231
232bool InputType::hasBadInput() const
233{
234    return false;
235}
236
237bool InputType::tooLong(const String&, HTMLTextFormControlElement::NeedsToCheckDirtyFlag) const
238{
239    return false;
240}
241
242bool InputType::patternMismatch(const String&) const
243{
244    return false;
245}
246
247bool InputType::rangeUnderflow(const String& value) const
248{
249    if (!isSteppable())
250        return false;
251
252    const Decimal numericValue = parseToNumberOrNaN(value);
253    if (!numericValue.isFinite())
254        return false;
255
256    return numericValue < createStepRange(RejectAny).minimum();
257}
258
259bool InputType::rangeOverflow(const String& value) const
260{
261    if (!isSteppable())
262        return false;
263
264    const Decimal numericValue = parseToNumberOrNaN(value);
265    if (!numericValue.isFinite())
266        return false;
267
268    return numericValue > createStepRange(RejectAny).maximum();
269}
270
271Decimal InputType::defaultValueForStepUp() const
272{
273    return 0;
274}
275
276double InputType::minimum() const
277{
278    return createStepRange(RejectAny).minimum().toDouble();
279}
280
281double InputType::maximum() const
282{
283    return createStepRange(RejectAny).maximum().toDouble();
284}
285
286bool InputType::isInRange(const String& value) const
287{
288    if (!isSteppable())
289        return false;
290
291    const Decimal numericValue = parseToNumberOrNaN(value);
292    if (!numericValue.isFinite())
293        return true;
294
295    StepRange stepRange(createStepRange(RejectAny));
296    return numericValue >= stepRange.minimum() && numericValue <= stepRange.maximum();
297}
298
299bool InputType::isOutOfRange(const String& value) const
300{
301    if (!isSteppable())
302        return false;
303
304    const Decimal numericValue = parseToNumberOrNaN(value);
305    if (!numericValue.isFinite())
306        return true;
307
308    StepRange stepRange(createStepRange(RejectAny));
309    return numericValue < stepRange.minimum() || numericValue > stepRange.maximum();
310}
311
312bool InputType::stepMismatch(const String& value) const
313{
314    if (!isSteppable())
315        return false;
316
317    const Decimal numericValue = parseToNumberOrNaN(value);
318    if (!numericValue.isFinite())
319        return false;
320
321    return createStepRange(RejectAny).stepMismatch(numericValue);
322}
323
324String InputType::badInputText() const
325{
326    ASSERT_NOT_REACHED();
327    return locale().queryString(WebLocalizedString::ValidationTypeMismatch);
328}
329
330String InputType::rangeOverflowText(const Decimal&) const
331{
332    ASSERT_NOT_REACHED();
333    return String();
334}
335
336String InputType::rangeUnderflowText(const Decimal&) const
337{
338    ASSERT_NOT_REACHED();
339    return String();
340}
341
342String InputType::typeMismatchText() const
343{
344    return locale().queryString(WebLocalizedString::ValidationTypeMismatch);
345}
346
347String InputType::valueMissingText() const
348{
349    return locale().queryString(WebLocalizedString::ValidationValueMissing);
350}
351
352String InputType::validationMessage() const
353{
354    const String value = element().value();
355
356    // The order of the following checks is meaningful. e.g. We'd like to show the
357    // badInput message even if the control has other validation errors.
358    if (hasBadInput())
359        return badInputText();
360
361    if (valueMissing(value))
362        return valueMissingText();
363
364    if (typeMismatch())
365        return typeMismatchText();
366
367    if (patternMismatch(value))
368        return locale().queryString(WebLocalizedString::ValidationPatternMismatch);
369
370    if (element().tooLong())
371        return locale().validationMessageTooLongText(value.length(), element().maxLength());
372
373    if (!isSteppable())
374        return emptyString();
375
376    const Decimal numericValue = parseToNumberOrNaN(value);
377    if (!numericValue.isFinite())
378        return emptyString();
379
380    StepRange stepRange(createStepRange(RejectAny));
381
382    if (numericValue < stepRange.minimum())
383        return rangeUnderflowText(stepRange.minimum());
384
385    if (numericValue > stepRange.maximum())
386        return rangeOverflowText(stepRange.maximum());
387
388    if (stepRange.stepMismatch(numericValue)) {
389        ASSERT(stepRange.hasStep());
390        Decimal candidate1 = stepRange.clampValue(numericValue);
391        String localizedCandidate1 = localizeValue(serialize(candidate1));
392        Decimal candidate2 = candidate1 < numericValue ? candidate1 + stepRange.step() : candidate1 - stepRange.step();
393        if (!candidate2.isFinite() || candidate2 < stepRange.minimum() || candidate2 > stepRange.maximum())
394            return locale().queryString(WebLocalizedString::ValidationStepMismatchCloseToLimit, localizedCandidate1);
395        String localizedCandidate2 = localizeValue(serialize(candidate2));
396        if (candidate1 < candidate2)
397            return locale().queryString(WebLocalizedString::ValidationStepMismatch, localizedCandidate1, localizedCandidate2);
398        return locale().queryString(WebLocalizedString::ValidationStepMismatch, localizedCandidate2, localizedCandidate1);
399    }
400
401    return emptyString();
402}
403
404bool InputType::shouldSubmitImplicitly(Event* event)
405{
406    return event->isKeyboardEvent() && event->type() == EventTypeNames::keypress && toKeyboardEvent(event)->charCode() == '\r';
407}
408
409Decimal InputType::parseToNumber(const String&, const Decimal& defaultValue) const
410{
411    ASSERT_NOT_REACHED();
412    return defaultValue;
413}
414
415Decimal InputType::parseToNumberOrNaN(const String& string) const
416{
417    return parseToNumber(string, Decimal::nan());
418}
419
420String InputType::serialize(const Decimal&) const
421{
422    ASSERT_NOT_REACHED();
423    return String();
424}
425
426void InputType::dispatchSimulatedClickIfActive(KeyboardEvent* event) const
427{
428    if (element().active())
429        element().dispatchSimulatedClick(event);
430    event->setDefaultHandled();
431}
432
433Chrome* InputType::chrome() const
434{
435    if (FrameHost* host = element().document().frameHost())
436        return &host->chrome();
437    return 0;
438}
439
440Locale& InputType::locale() const
441{
442    return element().locale();
443}
444
445bool InputType::canSetStringValue() const
446{
447    return true;
448}
449
450bool InputType::hasCustomFocusLogic() const
451{
452    return true;
453}
454
455bool InputType::isKeyboardFocusable() const
456{
457    return element().isFocusable();
458}
459
460bool InputType::shouldShowFocusRingOnMouseFocus() const
461{
462    return false;
463}
464
465void InputType::enableSecureTextInput()
466{
467}
468
469void InputType::disableSecureTextInput()
470{
471}
472
473void InputType::accessKeyAction(bool)
474{
475    element().focus(false);
476}
477
478void InputType::countUsage()
479{
480}
481
482bool InputType::shouldRespectAlignAttribute()
483{
484    return false;
485}
486
487void InputType::sanitizeValueInResponseToMinOrMaxAttributeChange()
488{
489}
490
491bool InputType::canBeSuccessfulSubmitButton()
492{
493    return false;
494}
495
496bool InputType::rendererIsNeeded()
497{
498    return true;
499}
500
501FileList* InputType::files()
502{
503    return 0;
504}
505
506void InputType::setFiles(PassRefPtrWillBeRawPtr<FileList>)
507{
508}
509
510bool InputType::getTypeSpecificValue(String&)
511{
512    return false;
513}
514
515String InputType::fallbackValue() const
516{
517    return String();
518}
519
520String InputType::defaultValue() const
521{
522    return String();
523}
524
525bool InputType::canSetSuggestedValue()
526{
527    return false;
528}
529
530bool InputType::shouldSendChangeEventAfterCheckedChanged()
531{
532    return true;
533}
534
535bool InputType::storesValueSeparateFromAttribute()
536{
537    return true;
538}
539
540bool InputType::shouldDispatchFormControlChangeEvent(String& oldValue, String& newValue)
541{
542    return !equalIgnoringNullity(oldValue, newValue);
543}
544
545void InputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
546{
547    element().setValueInternal(sanitizedValue, eventBehavior);
548    element().setNeedsStyleRecalc(SubtreeStyleChange);
549    if (!valueChanged)
550        return;
551    switch (eventBehavior) {
552    case DispatchChangeEvent:
553        element().dispatchFormControlChangeEvent();
554        break;
555    case DispatchInputAndChangeEvent:
556        element().dispatchFormControlInputEvent();
557        element().dispatchFormControlChangeEvent();
558        break;
559    case DispatchNoEvent:
560        break;
561    }
562}
563
564bool InputType::canSetValue(const String&)
565{
566    return true;
567}
568
569String InputType::localizeValue(const String& proposedValue) const
570{
571    return proposedValue;
572}
573
574String InputType::visibleValue() const
575{
576    return element().value();
577}
578
579String InputType::sanitizeValue(const String& proposedValue) const
580{
581    return proposedValue;
582}
583
584void InputType::warnIfValueIsInvalidAndElementIsVisible(const String& value) const
585{
586    // Don't warn if the value is set in Modernizr.
587    RenderStyle* style = element().renderStyle();
588    if (style && style->visibility() != HIDDEN)
589        warnIfValueIsInvalid(value);
590}
591
592void InputType::warnIfValueIsInvalid(const String&) const
593{
594}
595
596bool InputType::receiveDroppedFiles(const DragData*)
597{
598    ASSERT_NOT_REACHED();
599    return false;
600}
601
602String InputType::droppedFileSystemId()
603{
604    ASSERT_NOT_REACHED();
605    return String();
606}
607
608bool InputType::shouldRespectListAttribute()
609{
610    return false;
611}
612
613bool InputType::shouldRespectSpeechAttribute()
614{
615    return false;
616}
617
618bool InputType::isTextButton() const
619{
620    return false;
621}
622
623bool InputType::isInteractiveContent() const
624{
625    return true;
626}
627
628bool InputType::isEnumeratable()
629{
630    return true;
631}
632
633bool InputType::isCheckable()
634{
635    return false;
636}
637
638bool InputType::isSteppable() const
639{
640    return false;
641}
642
643bool InputType::shouldRespectHeightAndWidthAttributes()
644{
645    return false;
646}
647
648int InputType::maxLength() const
649{
650    return HTMLInputElement::maximumLength;
651}
652
653bool InputType::supportsPlaceholder() const
654{
655    return false;
656}
657
658bool InputType::supportsReadOnly() const
659{
660    return false;
661}
662
663String InputType::defaultToolTip() const
664{
665    return String();
666}
667
668Decimal InputType::findClosestTickMarkValue(const Decimal&)
669{
670    ASSERT_NOT_REACHED();
671    return Decimal::nan();
672}
673
674void InputType::handleDOMActivateEvent(Event*)
675{
676}
677
678bool InputType::hasLegalLinkAttribute(const QualifiedName&) const
679{
680    return false;
681}
682
683const QualifiedName& InputType::subResourceAttributeName() const
684{
685    return QualifiedName::null();
686}
687
688bool InputType::shouldAppearIndeterminate() const
689{
690    return false;
691}
692
693bool InputType::supportsInputModeAttribute() const
694{
695    return false;
696}
697
698bool InputType::supportsSelectionAPI() const
699{
700    return false;
701}
702
703unsigned InputType::height() const
704{
705    return 0;
706}
707
708unsigned InputType::width() const
709{
710    return 0;
711}
712
713TextDirection InputType::computedTextDirection()
714{
715    return element().computedStyle()->direction();
716}
717
718ColorChooserClient* InputType::colorChooserClient()
719{
720    return 0;
721}
722
723void InputType::applyStep(const Decimal& current, int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState)
724{
725    StepRange stepRange(createStepRange(anyStepHandling));
726    if (!stepRange.hasStep()) {
727        exceptionState.throwDOMException(InvalidStateError, "This form element does not have an allowed value step.");
728        return;
729    }
730
731    EventQueueScope scope;
732    const Decimal step = stepRange.step();
733
734    const AtomicString& stepString = element().fastGetAttribute(stepAttr);
735    if (!equalIgnoringCase(stepString, "any") && stepRange.stepMismatch(current)) {
736        // Snap-to-step / clamping steps
737        // If the current value is not matched to step value:
738        // - The value should be the larger matched value nearest to 0 if count > 0
739        //   e.g. <input type=number value=3 min=-100 step=3> -> 5
740        // - The value should be the smaller matched value nearest to 0 if count < 0
741        //   e.g. <input type=number value=3 min=-100 step=3> -> 2
742        //
743
744        ASSERT(!step.isZero());
745        Decimal newValue;
746        const Decimal base = stepRange.stepBase();
747        if (count < 0)
748            newValue = base + ((current - base) / step).floor() * step;
749        else if (count > 0)
750            newValue = base + ((current - base) / step).ceiling() * step;
751        else
752            newValue = current;
753
754        if (newValue < stepRange.minimum())
755            newValue = stepRange.minimum();
756        if (newValue > stepRange.maximum())
757            newValue = stepRange.maximum();
758
759        setValueAsDecimal(newValue, count == 1 || count == -1 ? DispatchChangeEvent : DispatchNoEvent, IGNORE_EXCEPTION);
760        if (count > 1) {
761            applyStep(newValue, count - 1, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
762            return;
763        }
764        if (count < -1) {
765            applyStep(newValue, count + 1, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
766            return;
767        }
768    } else {
769        Decimal newValue = current + stepRange.step() * count;
770
771        if (!equalIgnoringCase(stepString, "any"))
772            newValue = stepRange.alignValueForStep(current, newValue);
773
774        if (newValue > stepRange.maximum())
775            newValue = newValue - stepRange.step();
776        else if (newValue < stepRange.minimum())
777            newValue = newValue + stepRange.step();
778
779        setValueAsDecimal(newValue, eventBehavior, exceptionState);
780    }
781    if (AXObjectCache* cache = element().document().existingAXObjectCache())
782        cache->postNotification(&element(), AXObjectCache::AXValueChanged, true);
783}
784
785bool InputType::getAllowedValueStep(Decimal* step) const
786{
787    StepRange stepRange(createStepRange(RejectAny));
788    *step = stepRange.step();
789    return stepRange.hasStep();
790}
791
792StepRange InputType::createStepRange(AnyStepHandling) const
793{
794    ASSERT_NOT_REACHED();
795    return StepRange();
796}
797
798void InputType::stepUp(int n, ExceptionState& exceptionState)
799{
800    if (!isSteppable()) {
801        exceptionState.throwDOMException(InvalidStateError, "This form element is not steppable.");
802        return;
803    }
804    const Decimal current = parseToNumber(element().value(), 0);
805    applyStep(current, n, RejectAny, DispatchNoEvent, exceptionState);
806}
807
808void InputType::stepUpFromRenderer(int n)
809{
810    // The only difference from stepUp()/stepDown() is the extra treatment
811    // of the current value before applying the step:
812    //
813    // If the current value is not a number, including empty, the current value is assumed as 0.
814    //   * If 0 is in-range, and matches to step value
815    //     - The value should be the +step if n > 0
816    //     - The value should be the -step if n < 0
817    //     If -step or +step is out of range, new value should be 0.
818    //   * If 0 is smaller than the minimum value
819    //     - The value should be the minimum value for any n
820    //   * If 0 is larger than the maximum value
821    //     - The value should be the maximum value for any n
822    //   * If 0 is in-range, but not matched to step value
823    //     - The value should be the larger matched value nearest to 0 if n > 0
824    //       e.g. <input type=number min=-100 step=3> -> 2
825    //     - The value should be the smaler matched value nearest to 0 if n < 0
826    //       e.g. <input type=number min=-100 step=3> -> -1
827    //   As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time".
828    //   As for datetime type, the current value is assumed as "the current date/time in UTC".
829    // If the current value is smaller than the minimum value:
830    //  - The value should be the minimum value if n > 0
831    //  - Nothing should happen if n < 0
832    // If the current value is larger than the maximum value:
833    //  - The value should be the maximum value if n < 0
834    //  - Nothing should happen if n > 0
835    //
836    // n is assumed as -n if step < 0.
837
838    ASSERT(isSteppable());
839    if (!isSteppable())
840        return;
841    ASSERT(n);
842    if (!n)
843        return;
844
845    StepRange stepRange(createStepRange(AnyIsDefaultStep));
846
847    // FIXME: Not any changes after stepping, even if it is an invalid value, may be better.
848    // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => "foo")
849    if (!stepRange.hasStep())
850        return;
851
852    EventQueueScope scope;
853    const Decimal step = stepRange.step();
854
855    int sign;
856    if (step > 0)
857        sign = n;
858    else if (step < 0)
859        sign = -n;
860    else
861        sign = 0;
862
863    Decimal current = parseToNumberOrNaN(element().value());
864    if (!current.isFinite()) {
865        current = defaultValueForStepUp();
866        const Decimal nextDiff = step * n;
867        if (current < stepRange.minimum() - nextDiff)
868            current = stepRange.minimum() - nextDiff;
869        if (current > stepRange.maximum() - nextDiff)
870            current = stepRange.maximum() - nextDiff;
871        setValueAsDecimal(current, DispatchNoEvent, IGNORE_EXCEPTION);
872    }
873    if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum())) {
874        setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchChangeEvent, IGNORE_EXCEPTION);
875        return;
876    }
877    applyStep(current, n, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
878}
879
880void InputType::countUsageIfVisible(UseCounter::Feature feature) const
881{
882    if (RenderStyle* style = element().renderStyle()) {
883        if (style->visibility() != HIDDEN)
884            UseCounter::count(element().document(), feature);
885    }
886}
887
888Decimal InputType::findStepBase(const Decimal& defaultValue) const
889{
890    Decimal stepBase = parseToNumber(element().fastGetAttribute(minAttr), Decimal::nan());
891    if (!stepBase.isFinite())
892        stepBase = parseToNumber(element().fastGetAttribute(valueAttr), defaultValue);
893    return stepBase;
894}
895
896StepRange InputType::createStepRange(AnyStepHandling anyStepHandling, const Decimal& stepBaseDefault, const Decimal& minimumDefault, const Decimal& maximumDefault, const StepRange::StepDescription& stepDescription) const
897{
898    const Decimal stepBase = findStepBase(stepBaseDefault);
899    const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), minimumDefault);
900    const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), maximumDefault);
901    const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
902    return StepRange(stepBase, minimum, maximum, step, stepDescription);
903}
904
905} // namespace blink
906