• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/v8/ExceptionMessages.h"
32 #include "bindings/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/ColorInputType.h"
46 #include "core/html/forms/DateInputType.h"
47 #include "core/html/forms/DateTimeLocalInputType.h"
48 #include "core/html/forms/EmailInputType.h"
49 #include "core/html/forms/FileInputType.h"
50 #include "core/html/forms/FormController.h"
51 #include "core/html/forms/HiddenInputType.h"
52 #include "core/html/forms/ImageInputType.h"
53 #include "core/html/forms/MonthInputType.h"
54 #include "core/html/forms/NumberInputType.h"
55 #include "core/html/forms/PasswordInputType.h"
56 #include "core/html/forms/RadioInputType.h"
57 #include "core/html/forms/RangeInputType.h"
58 #include "core/html/forms/ResetInputType.h"
59 #include "core/html/forms/SearchInputType.h"
60 #include "core/html/forms/SubmitInputType.h"
61 #include "core/html/forms/TelephoneInputType.h"
62 #include "core/html/forms/TextInputType.h"
63 #include "core/html/forms/TimeInputType.h"
64 #include "core/html/forms/URLInputType.h"
65 #include "core/html/forms/WeekInputType.h"
66 #include "core/html/parser/HTMLParserIdioms.h"
67 #include "core/rendering/RenderTheme.h"
68 #include "platform/ColorChooser.h"
69 #include "platform/RuntimeEnabledFeatures.h"
70 #include "platform/text/PlatformLocale.h"
71 #include "platform/text/TextBreakIterator.h"
72 
73 namespace WebCore {
74 
75 using blink::WebLocalizedString;
76 using namespace HTMLNames;
77 
78 typedef PassRefPtrWillBeRawPtr<InputType> (*InputTypeFactoryFunction)(HTMLInputElement&);
79 typedef HashMap<AtomicString, InputTypeFactoryFunction, CaseFoldingHash> InputTypeFactoryMap;
80 
createInputTypeFactoryMap()81 static 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 
factoryMap()109 static const InputTypeFactoryMap* factoryMap()
110 {
111     static const InputTypeFactoryMap* factoryMap = createInputTypeFactoryMap().leakPtr();
112     return factoryMap;
113 }
114 
create(HTMLInputElement & element,const AtomicString & typeName)115 PassRefPtrWillBeRawPtr<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 
createText(HTMLInputElement & element)123 PassRefPtrWillBeRawPtr<InputType> InputType::createText(HTMLInputElement& element)
124 {
125     return TextInputType::create(element);
126 }
127 
normalizeTypeName(const AtomicString & typeName)128 const 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 
~InputType()136 InputType::~InputType()
137 {
138 }
139 
isTextField() const140 bool InputType::isTextField() const
141 {
142     return false;
143 }
144 
isTextType() const145 bool InputType::isTextType() const
146 {
147     return false;
148 }
149 
isRangeControl() const150 bool InputType::isRangeControl() const
151 {
152     return false;
153 }
154 
shouldSaveAndRestoreFormControlState() const155 bool InputType::shouldSaveAndRestoreFormControlState() const
156 {
157     return true;
158 }
159 
saveFormControlState() const160 FormControlState InputType::saveFormControlState() const
161 {
162     String currentValue = element().value();
163     if (currentValue == element().defaultValue())
164         return FormControlState();
165     return FormControlState(currentValue);
166 }
167 
restoreFormControlState(const FormControlState & state)168 void InputType::restoreFormControlState(const FormControlState& state)
169 {
170     element().setValue(state[0]);
171 }
172 
isFormDataAppendable() const173 bool InputType::isFormDataAppendable() const
174 {
175     // There is no form data unless there's a name for non-image types.
176     return !element().name().isEmpty();
177 }
178 
appendFormData(FormDataList & encoding,bool) const179 bool InputType::appendFormData(FormDataList& encoding, bool) const
180 {
181     // Always successful.
182     encoding.appendData(element().name(), element().value());
183     return true;
184 }
185 
resultForDialogSubmit() const186 String InputType::resultForDialogSubmit() const
187 {
188     return element().fastGetAttribute(valueAttr);
189 }
190 
valueAsDate() const191 double InputType::valueAsDate() const
192 {
193     return DateComponents::invalidMilliseconds();
194 }
195 
setValueAsDate(double,ExceptionState & exceptionState) const196 void InputType::setValueAsDate(double, ExceptionState& exceptionState) const
197 {
198     exceptionState.throwDOMException(InvalidStateError, "This input element does not support Date values.");
199 }
200 
valueAsDouble() const201 double InputType::valueAsDouble() const
202 {
203     return std::numeric_limits<double>::quiet_NaN();
204 }
205 
setValueAsDouble(double doubleValue,TextFieldEventBehavior eventBehavior,ExceptionState & exceptionState) const206 void InputType::setValueAsDouble(double doubleValue, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState) const
207 {
208     exceptionState.throwDOMException(InvalidStateError, "This input element does not support Number values.");
209 }
210 
setValueAsDecimal(const Decimal & newValue,TextFieldEventBehavior eventBehavior,ExceptionState &) const211 void InputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior, ExceptionState&) const
212 {
213     element().setValue(serialize(newValue), eventBehavior);
214 }
215 
supportsValidation() const216 bool InputType::supportsValidation() const
217 {
218     return true;
219 }
220 
typeMismatchFor(const String &) const221 bool InputType::typeMismatchFor(const String&) const
222 {
223     return false;
224 }
225 
typeMismatch() const226 bool InputType::typeMismatch() const
227 {
228     return false;
229 }
230 
supportsRequired() const231 bool InputType::supportsRequired() const
232 {
233     // Almost all validatable types support @required.
234     return supportsValidation();
235 }
236 
valueMissing(const String &) const237 bool InputType::valueMissing(const String&) const
238 {
239     return false;
240 }
241 
hasBadInput() const242 bool InputType::hasBadInput() const
243 {
244     return false;
245 }
246 
patternMismatch(const String &) const247 bool InputType::patternMismatch(const String&) const
248 {
249     return false;
250 }
251 
rangeUnderflow(const String & value) const252 bool InputType::rangeUnderflow(const String& value) const
253 {
254     if (!isSteppable())
255         return false;
256 
257     const Decimal numericValue = parseToNumberOrNaN(value);
258     if (!numericValue.isFinite())
259         return false;
260 
261     return numericValue < createStepRange(RejectAny).minimum();
262 }
263 
rangeOverflow(const String & value) const264 bool InputType::rangeOverflow(const String& value) const
265 {
266     if (!isSteppable())
267         return false;
268 
269     const Decimal numericValue = parseToNumberOrNaN(value);
270     if (!numericValue.isFinite())
271         return false;
272 
273     return numericValue > createStepRange(RejectAny).maximum();
274 }
275 
defaultValueForStepUp() const276 Decimal InputType::defaultValueForStepUp() const
277 {
278     return 0;
279 }
280 
minimum() const281 double InputType::minimum() const
282 {
283     return createStepRange(RejectAny).minimum().toDouble();
284 }
285 
maximum() const286 double InputType::maximum() const
287 {
288     return createStepRange(RejectAny).maximum().toDouble();
289 }
290 
isInRange(const String & value) const291 bool InputType::isInRange(const String& value) const
292 {
293     if (!isSteppable())
294         return false;
295 
296     const Decimal numericValue = parseToNumberOrNaN(value);
297     if (!numericValue.isFinite())
298         return true;
299 
300     StepRange stepRange(createStepRange(RejectAny));
301     return numericValue >= stepRange.minimum() && numericValue <= stepRange.maximum();
302 }
303 
isOutOfRange(const String & value) const304 bool InputType::isOutOfRange(const String& value) const
305 {
306     if (!isSteppable())
307         return false;
308 
309     const Decimal numericValue = parseToNumberOrNaN(value);
310     if (!numericValue.isFinite())
311         return true;
312 
313     StepRange stepRange(createStepRange(RejectAny));
314     return numericValue < stepRange.minimum() || numericValue > stepRange.maximum();
315 }
316 
stepMismatch(const String & value) const317 bool InputType::stepMismatch(const String& value) const
318 {
319     if (!isSteppable())
320         return false;
321 
322     const Decimal numericValue = parseToNumberOrNaN(value);
323     if (!numericValue.isFinite())
324         return false;
325 
326     return createStepRange(RejectAny).stepMismatch(numericValue);
327 }
328 
badInputText() const329 String InputType::badInputText() const
330 {
331     ASSERT_NOT_REACHED();
332     return locale().queryString(WebLocalizedString::ValidationTypeMismatch);
333 }
334 
rangeOverflowText(const Decimal &) const335 String InputType::rangeOverflowText(const Decimal&) const
336 {
337     ASSERT_NOT_REACHED();
338     return String();
339 }
340 
rangeUnderflowText(const Decimal &) const341 String InputType::rangeUnderflowText(const Decimal&) const
342 {
343     ASSERT_NOT_REACHED();
344     return String();
345 }
346 
typeMismatchText() const347 String InputType::typeMismatchText() const
348 {
349     return locale().queryString(WebLocalizedString::ValidationTypeMismatch);
350 }
351 
valueMissingText() const352 String InputType::valueMissingText() const
353 {
354     return locale().queryString(WebLocalizedString::ValidationValueMissing);
355 }
356 
validationMessage() const357 String InputType::validationMessage() const
358 {
359     const String value = element().value();
360 
361     // The order of the following checks is meaningful. e.g. We'd like to show the
362     // badInput message even if the control has other validation errors.
363     if (hasBadInput())
364         return badInputText();
365 
366     if (valueMissing(value))
367         return valueMissingText();
368 
369     if (typeMismatch())
370         return typeMismatchText();
371 
372     if (patternMismatch(value))
373         return locale().queryString(WebLocalizedString::ValidationPatternMismatch);
374 
375     if (element().tooLong())
376         return locale().validationMessageTooLongText(value.length(), element().maxLength());
377 
378     if (!isSteppable())
379         return emptyString();
380 
381     const Decimal numericValue = parseToNumberOrNaN(value);
382     if (!numericValue.isFinite())
383         return emptyString();
384 
385     StepRange stepRange(createStepRange(RejectAny));
386 
387     if (numericValue < stepRange.minimum())
388         return rangeUnderflowText(stepRange.minimum());
389 
390     if (numericValue > stepRange.maximum())
391         return rangeOverflowText(stepRange.maximum());
392 
393     if (stepRange.stepMismatch(numericValue)) {
394         ASSERT(stepRange.hasStep());
395         Decimal candidate1 = stepRange.clampValue(numericValue);
396         String localizedCandidate1 = localizeValue(serialize(candidate1));
397         Decimal candidate2 = candidate1 < numericValue ? candidate1 + stepRange.step() : candidate1 - stepRange.step();
398         if (!candidate2.isFinite() || candidate2 < stepRange.minimum() || candidate2 > stepRange.maximum())
399             return locale().queryString(WebLocalizedString::ValidationStepMismatchCloseToLimit, localizedCandidate1);
400         String localizedCandidate2 = localizeValue(serialize(candidate2));
401         if (candidate1 < candidate2)
402             return locale().queryString(WebLocalizedString::ValidationStepMismatch, localizedCandidate1, localizedCandidate2);
403         return locale().queryString(WebLocalizedString::ValidationStepMismatch, localizedCandidate2, localizedCandidate1);
404     }
405 
406     return emptyString();
407 }
408 
shouldSubmitImplicitly(Event * event)409 bool InputType::shouldSubmitImplicitly(Event* event)
410 {
411     return event->isKeyboardEvent() && event->type() == EventTypeNames::keypress && toKeyboardEvent(event)->charCode() == '\r';
412 }
413 
parseToNumber(const String &,const Decimal & defaultValue) const414 Decimal InputType::parseToNumber(const String&, const Decimal& defaultValue) const
415 {
416     ASSERT_NOT_REACHED();
417     return defaultValue;
418 }
419 
parseToNumberOrNaN(const String & string) const420 Decimal InputType::parseToNumberOrNaN(const String& string) const
421 {
422     return parseToNumber(string, Decimal::nan());
423 }
424 
serialize(const Decimal &) const425 String InputType::serialize(const Decimal&) const
426 {
427     ASSERT_NOT_REACHED();
428     return String();
429 }
430 
dispatchSimulatedClickIfActive(KeyboardEvent * event) const431 void InputType::dispatchSimulatedClickIfActive(KeyboardEvent* event) const
432 {
433     if (element().active())
434         element().dispatchSimulatedClick(event);
435     event->setDefaultHandled();
436 }
437 
chrome() const438 Chrome* InputType::chrome() const
439 {
440     if (FrameHost* host = element().document().frameHost())
441         return &host->chrome();
442     return 0;
443 }
444 
locale() const445 Locale& InputType::locale() const
446 {
447     return element().locale();
448 }
449 
canSetStringValue() const450 bool InputType::canSetStringValue() const
451 {
452     return true;
453 }
454 
hasCustomFocusLogic() const455 bool InputType::hasCustomFocusLogic() const
456 {
457     return true;
458 }
459 
isKeyboardFocusable() const460 bool InputType::isKeyboardFocusable() const
461 {
462     return element().isFocusable();
463 }
464 
shouldShowFocusRingOnMouseFocus() const465 bool InputType::shouldShowFocusRingOnMouseFocus() const
466 {
467     return false;
468 }
469 
shouldUseInputMethod() const470 bool InputType::shouldUseInputMethod() const
471 {
472     return false;
473 }
474 
enableSecureTextInput()475 void InputType::enableSecureTextInput()
476 {
477 }
478 
disableSecureTextInput()479 void InputType::disableSecureTextInput()
480 {
481 }
482 
accessKeyAction(bool)483 void InputType::accessKeyAction(bool)
484 {
485     element().focus(false);
486 }
487 
countUsage()488 void InputType::countUsage()
489 {
490 }
491 
shouldRespectAlignAttribute()492 bool InputType::shouldRespectAlignAttribute()
493 {
494     return false;
495 }
496 
sanitizeValueInResponseToMinOrMaxAttributeChange()497 void InputType::sanitizeValueInResponseToMinOrMaxAttributeChange()
498 {
499 }
500 
canBeSuccessfulSubmitButton()501 bool InputType::canBeSuccessfulSubmitButton()
502 {
503     return false;
504 }
505 
rendererIsNeeded()506 bool InputType::rendererIsNeeded()
507 {
508     return true;
509 }
510 
files()511 FileList* InputType::files()
512 {
513     return 0;
514 }
515 
setFiles(PassRefPtrWillBeRawPtr<FileList>)516 void InputType::setFiles(PassRefPtrWillBeRawPtr<FileList>)
517 {
518 }
519 
getTypeSpecificValue(String &)520 bool InputType::getTypeSpecificValue(String&)
521 {
522     return false;
523 }
524 
fallbackValue() const525 String InputType::fallbackValue() const
526 {
527     return String();
528 }
529 
defaultValue() const530 String InputType::defaultValue() const
531 {
532     return String();
533 }
534 
canSetSuggestedValue()535 bool InputType::canSetSuggestedValue()
536 {
537     return false;
538 }
539 
shouldSendChangeEventAfterCheckedChanged()540 bool InputType::shouldSendChangeEventAfterCheckedChanged()
541 {
542     return true;
543 }
544 
storesValueSeparateFromAttribute()545 bool InputType::storesValueSeparateFromAttribute()
546 {
547     return true;
548 }
549 
shouldDispatchFormControlChangeEvent(String & oldValue,String & newValue)550 bool InputType::shouldDispatchFormControlChangeEvent(String& oldValue, String& newValue)
551 {
552     return !equalIgnoringNullity(oldValue, newValue);
553 }
554 
setValue(const String & sanitizedValue,bool valueChanged,TextFieldEventBehavior eventBehavior)555 void InputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
556 {
557     element().setValueInternal(sanitizedValue, eventBehavior);
558     element().setNeedsStyleRecalc(SubtreeStyleChange);
559     if (!valueChanged)
560         return;
561     switch (eventBehavior) {
562     case DispatchChangeEvent:
563         element().dispatchFormControlChangeEvent();
564         break;
565     case DispatchInputAndChangeEvent:
566         element().dispatchFormControlInputEvent();
567         element().dispatchFormControlChangeEvent();
568         break;
569     case DispatchNoEvent:
570         break;
571     }
572 }
573 
canSetValue(const String &)574 bool InputType::canSetValue(const String&)
575 {
576     return true;
577 }
578 
localizeValue(const String & proposedValue) const579 String InputType::localizeValue(const String& proposedValue) const
580 {
581     return proposedValue;
582 }
583 
visibleValue() const584 String InputType::visibleValue() const
585 {
586     return element().value();
587 }
588 
sanitizeValue(const String & proposedValue) const589 String InputType::sanitizeValue(const String& proposedValue) const
590 {
591     return proposedValue;
592 }
593 
receiveDroppedFiles(const DragData *)594 bool InputType::receiveDroppedFiles(const DragData*)
595 {
596     ASSERT_NOT_REACHED();
597     return false;
598 }
599 
droppedFileSystemId()600 String InputType::droppedFileSystemId()
601 {
602     ASSERT_NOT_REACHED();
603     return String();
604 }
605 
shouldRespectListAttribute()606 bool InputType::shouldRespectListAttribute()
607 {
608     return false;
609 }
610 
shouldRespectSpeechAttribute()611 bool InputType::shouldRespectSpeechAttribute()
612 {
613     return false;
614 }
615 
isTextButton() const616 bool InputType::isTextButton() const
617 {
618     return false;
619 }
620 
isRadioButton() const621 bool InputType::isRadioButton() const
622 {
623     return false;
624 }
625 
isSearchField() const626 bool InputType::isSearchField() const
627 {
628     return false;
629 }
630 
isHiddenType() const631 bool InputType::isHiddenType() const
632 {
633     return false;
634 }
635 
isPasswordField() const636 bool InputType::isPasswordField() const
637 {
638     return false;
639 }
640 
isCheckbox() const641 bool InputType::isCheckbox() const
642 {
643     return false;
644 }
645 
isEmailField() const646 bool InputType::isEmailField() const
647 {
648     return false;
649 }
650 
isFileUpload() const651 bool InputType::isFileUpload() const
652 {
653     return false;
654 }
655 
isImageButton() const656 bool InputType::isImageButton() const
657 {
658     return false;
659 }
660 
isInteractiveContent() const661 bool InputType::isInteractiveContent() const
662 {
663     return true;
664 }
665 
isNumberField() const666 bool InputType::isNumberField() const
667 {
668     return false;
669 }
670 
isTelephoneField() const671 bool InputType::isTelephoneField() const
672 {
673     return false;
674 }
675 
isURLField() const676 bool InputType::isURLField() const
677 {
678     return false;
679 }
680 
isDateField() const681 bool InputType::isDateField() const
682 {
683     return false;
684 }
685 
isDateTimeLocalField() const686 bool InputType::isDateTimeLocalField() const
687 {
688     return false;
689 }
690 
isMonthField() const691 bool InputType::isMonthField() const
692 {
693     return false;
694 }
695 
isTimeField() const696 bool InputType::isTimeField() const
697 {
698     return false;
699 }
700 
isWeekField() const701 bool InputType::isWeekField() const
702 {
703     return false;
704 }
705 
isEnumeratable()706 bool InputType::isEnumeratable()
707 {
708     return true;
709 }
710 
isCheckable()711 bool InputType::isCheckable()
712 {
713     return false;
714 }
715 
isSteppable() const716 bool InputType::isSteppable() const
717 {
718     return false;
719 }
720 
isColorControl() const721 bool InputType::isColorControl() const
722 {
723     return false;
724 }
725 
shouldRespectHeightAndWidthAttributes()726 bool InputType::shouldRespectHeightAndWidthAttributes()
727 {
728     return false;
729 }
730 
supportsPlaceholder() const731 bool InputType::supportsPlaceholder() const
732 {
733     return false;
734 }
735 
supportsReadOnly() const736 bool InputType::supportsReadOnly() const
737 {
738     return false;
739 }
740 
defaultToolTip() const741 String InputType::defaultToolTip() const
742 {
743     return String();
744 }
745 
findClosestTickMarkValue(const Decimal &)746 Decimal InputType::findClosestTickMarkValue(const Decimal&)
747 {
748     ASSERT_NOT_REACHED();
749     return Decimal::nan();
750 }
751 
handleDOMActivateEvent(Event *)752 void InputType::handleDOMActivateEvent(Event*)
753 {
754 }
755 
hasLegalLinkAttribute(const QualifiedName &) const756 bool InputType::hasLegalLinkAttribute(const QualifiedName&) const
757 {
758     return false;
759 }
760 
subResourceAttributeName() const761 const QualifiedName& InputType::subResourceAttributeName() const
762 {
763     return QualifiedName::null();
764 }
765 
supportsIndeterminateAppearance() const766 bool InputType::supportsIndeterminateAppearance() const
767 {
768     return false;
769 }
770 
supportsInputModeAttribute() const771 bool InputType::supportsInputModeAttribute() const
772 {
773     return false;
774 }
775 
supportsSelectionAPI() const776 bool InputType::supportsSelectionAPI() const
777 {
778     return false;
779 }
780 
height() const781 unsigned InputType::height() const
782 {
783     return 0;
784 }
785 
width() const786 unsigned InputType::width() const
787 {
788     return 0;
789 }
790 
applyStep(const Decimal & current,int count,AnyStepHandling anyStepHandling,TextFieldEventBehavior eventBehavior,ExceptionState & exceptionState)791 void InputType::applyStep(const Decimal& current, int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState)
792 {
793     StepRange stepRange(createStepRange(anyStepHandling));
794     if (!stepRange.hasStep()) {
795         exceptionState.throwDOMException(InvalidStateError, "This form element does not have an allowed value step.");
796         return;
797     }
798 
799     EventQueueScope scope;
800     const Decimal step = stepRange.step();
801 
802     const AtomicString& stepString = element().fastGetAttribute(stepAttr);
803     if (!equalIgnoringCase(stepString, "any") && stepRange.stepMismatch(current)) {
804         // Snap-to-step / clamping steps
805         // If the current value is not matched to step value:
806         // - The value should be the larger matched value nearest to 0 if count > 0
807         //   e.g. <input type=number value=3 min=-100 step=3> -> 5
808         // - The value should be the smaller matched value nearest to 0 if count < 0
809         //   e.g. <input type=number value=3 min=-100 step=3> -> 2
810         //
811 
812         ASSERT(!step.isZero());
813         Decimal newValue;
814         const Decimal base = stepRange.stepBase();
815         if (count < 0)
816             newValue = base + ((current - base) / step).floor() * step;
817         else if (count > 0)
818             newValue = base + ((current - base) / step).ceiling() * step;
819         else
820             newValue = current;
821 
822         if (newValue < stepRange.minimum())
823             newValue = stepRange.minimum();
824         if (newValue > stepRange.maximum())
825             newValue = stepRange.maximum();
826 
827         setValueAsDecimal(newValue, count == 1 || count == -1 ? DispatchChangeEvent : DispatchNoEvent, IGNORE_EXCEPTION);
828         if (count > 1) {
829             applyStep(newValue, count - 1, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
830             return;
831         }
832         if (count < -1) {
833             applyStep(newValue, count + 1, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
834             return;
835         }
836     } else {
837         Decimal newValue = current + stepRange.step() * count;
838 
839         if (!equalIgnoringCase(stepString, "any"))
840             newValue = stepRange.alignValueForStep(current, newValue);
841 
842         if (newValue > stepRange.maximum())
843             newValue = newValue - stepRange.step();
844         else if (newValue < stepRange.minimum())
845             newValue = newValue + stepRange.step();
846 
847         setValueAsDecimal(newValue, eventBehavior, exceptionState);
848     }
849     if (AXObjectCache* cache = element().document().existingAXObjectCache())
850         cache->postNotification(&element(), AXObjectCache::AXValueChanged, true);
851 }
852 
getAllowedValueStep(Decimal * step) const853 bool InputType::getAllowedValueStep(Decimal* step) const
854 {
855     StepRange stepRange(createStepRange(RejectAny));
856     *step = stepRange.step();
857     return stepRange.hasStep();
858 }
859 
createStepRange(AnyStepHandling) const860 StepRange InputType::createStepRange(AnyStepHandling) const
861 {
862     ASSERT_NOT_REACHED();
863     return StepRange();
864 }
865 
stepUp(int n,ExceptionState & exceptionState)866 void InputType::stepUp(int n, ExceptionState& exceptionState)
867 {
868     if (!isSteppable()) {
869         exceptionState.throwDOMException(InvalidStateError, "This form element is not steppable.");
870         return;
871     }
872     const Decimal current = parseToNumber(element().value(), 0);
873     applyStep(current, n, RejectAny, DispatchNoEvent, exceptionState);
874 }
875 
stepUpFromRenderer(int n)876 void InputType::stepUpFromRenderer(int n)
877 {
878     // The only difference from stepUp()/stepDown() is the extra treatment
879     // of the current value before applying the step:
880     //
881     // If the current value is not a number, including empty, the current value is assumed as 0.
882     //   * If 0 is in-range, and matches to step value
883     //     - The value should be the +step if n > 0
884     //     - The value should be the -step if n < 0
885     //     If -step or +step is out of range, new value should be 0.
886     //   * If 0 is smaller than the minimum value
887     //     - The value should be the minimum value for any n
888     //   * If 0 is larger than the maximum value
889     //     - The value should be the maximum value for any n
890     //   * If 0 is in-range, but not matched to step value
891     //     - The value should be the larger matched value nearest to 0 if n > 0
892     //       e.g. <input type=number min=-100 step=3> -> 2
893     //     - The value should be the smaler matched value nearest to 0 if n < 0
894     //       e.g. <input type=number min=-100 step=3> -> -1
895     //   As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time".
896     //   As for datetime type, the current value is assumed as "the current date/time in UTC".
897     // If the current value is smaller than the minimum value:
898     //  - The value should be the minimum value if n > 0
899     //  - Nothing should happen if n < 0
900     // If the current value is larger than the maximum value:
901     //  - The value should be the maximum value if n < 0
902     //  - Nothing should happen if n > 0
903     //
904     // n is assumed as -n if step < 0.
905 
906     ASSERT(isSteppable());
907     if (!isSteppable())
908         return;
909     ASSERT(n);
910     if (!n)
911         return;
912 
913     StepRange stepRange(createStepRange(AnyIsDefaultStep));
914 
915     // FIXME: Not any changes after stepping, even if it is an invalid value, may be better.
916     // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => "foo")
917     if (!stepRange.hasStep())
918         return;
919 
920     EventQueueScope scope;
921     const Decimal step = stepRange.step();
922 
923     int sign;
924     if (step > 0)
925         sign = n;
926     else if (step < 0)
927         sign = -n;
928     else
929         sign = 0;
930 
931     Decimal current = parseToNumberOrNaN(element().value());
932     if (!current.isFinite()) {
933         current = defaultValueForStepUp();
934         const Decimal nextDiff = step * n;
935         if (current < stepRange.minimum() - nextDiff)
936             current = stepRange.minimum() - nextDiff;
937         if (current > stepRange.maximum() - nextDiff)
938             current = stepRange.maximum() - nextDiff;
939         setValueAsDecimal(current, DispatchNoEvent, IGNORE_EXCEPTION);
940     }
941     if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum())) {
942         setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchChangeEvent, IGNORE_EXCEPTION);
943         return;
944     }
945     applyStep(current, n, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
946 }
947 
countUsageIfVisible(UseCounter::Feature feature) const948 void InputType::countUsageIfVisible(UseCounter::Feature feature) const
949 {
950     if (RenderStyle* style = element().renderStyle()) {
951         if (style->visibility() != HIDDEN)
952             UseCounter::count(element().document(), feature);
953     }
954 }
955 
findStepBase(const Decimal & defaultValue) const956 Decimal InputType::findStepBase(const Decimal& defaultValue) const
957 {
958     Decimal stepBase = parseToNumber(element().fastGetAttribute(minAttr), Decimal::nan());
959     if (!stepBase.isFinite())
960         stepBase = parseToNumber(element().fastGetAttribute(valueAttr), defaultValue);
961     return stepBase;
962 }
963 
createStepRange(AnyStepHandling anyStepHandling,const Decimal & stepBaseDefault,const Decimal & minimumDefault,const Decimal & maximumDefault,const StepRange::StepDescription & stepDescription) const964 StepRange InputType::createStepRange(AnyStepHandling anyStepHandling, const Decimal& stepBaseDefault, const Decimal& minimumDefault, const Decimal& maximumDefault, const StepRange::StepDescription& stepDescription) const
965 {
966     const Decimal stepBase = findStepBase(stepBaseDefault);
967     const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), minimumDefault);
968     const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), maximumDefault);
969     const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
970     return StepRange(stepBase, minimum, maximum, step, stepDescription);
971 }
972 
973 } // namespace WebCore
974