• 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 "InputTypeNames.h"
32 #include "RuntimeEnabledFeatures.h"
33 #include "core/accessibility/AXObjectCache.h"
34 #include "core/dom/NodeRenderStyle.h"
35 #include "core/events/KeyboardEvent.h"
36 #include "core/events/ScopedEventQueue.h"
37 #include "core/fileapi/FileList.h"
38 #include "core/html/FormDataList.h"
39 #include "core/html/HTMLInputElement.h"
40 #include "core/html/forms/ButtonInputType.h"
41 #include "core/html/forms/CheckboxInputType.h"
42 #include "core/html/forms/ColorInputType.h"
43 #include "core/html/forms/DateInputType.h"
44 #include "core/html/forms/DateTimeLocalInputType.h"
45 #include "core/html/forms/EmailInputType.h"
46 #include "core/html/forms/FileInputType.h"
47 #include "core/html/forms/FormController.h"
48 #include "core/html/forms/HiddenInputType.h"
49 #include "core/html/forms/ImageInputType.h"
50 #include "core/html/forms/MonthInputType.h"
51 #include "core/html/forms/NumberInputType.h"
52 #include "core/html/forms/PasswordInputType.h"
53 #include "core/html/forms/RadioInputType.h"
54 #include "core/html/forms/RangeInputType.h"
55 #include "core/html/forms/ResetInputType.h"
56 #include "core/html/forms/SearchInputType.h"
57 #include "core/html/forms/SubmitInputType.h"
58 #include "core/html/forms/TelephoneInputType.h"
59 #include "core/html/forms/TextInputType.h"
60 #include "core/html/forms/TimeInputType.h"
61 #include "core/html/forms/URLInputType.h"
62 #include "core/html/forms/WeekInputType.h"
63 #include "core/html/parser/HTMLParserIdioms.h"
64 #include "core/html/shadow/HTMLShadowElement.h"
65 #include "core/page/Page.h"
66 #include "core/rendering/RenderTheme.h"
67 #include "platform/text/PlatformLocale.h"
68 #include "platform/text/TextBreakIterator.h"
69 
70 namespace WebCore {
71 
72 using blink::WebLocalizedString;
73 using namespace HTMLNames;
74 using namespace std;
75 
76 typedef PassRefPtr<InputType> (*InputTypeFactoryFunction)(HTMLInputElement&);
77 typedef HashMap<AtomicString, InputTypeFactoryFunction, CaseFoldingHash> InputTypeFactoryMap;
78 
createInputTypeFactoryMap()79 static PassOwnPtr<InputTypeFactoryMap> createInputTypeFactoryMap()
80 {
81     OwnPtr<InputTypeFactoryMap> map = adoptPtr(new InputTypeFactoryMap);
82     map->add(InputTypeNames::button, ButtonInputType::create);
83     map->add(InputTypeNames::checkbox, CheckboxInputType::create);
84     if (RuntimeEnabledFeatures::inputTypeColorEnabled())
85         map->add(InputTypeNames::color, ColorInputType::create);
86     map->add(InputTypeNames::date, DateInputType::create);
87     map->add(InputTypeNames::datetime_local, DateTimeLocalInputType::create);
88     map->add(InputTypeNames::email, EmailInputType::create);
89     map->add(InputTypeNames::file, FileInputType::create);
90     map->add(InputTypeNames::hidden, HiddenInputType::create);
91     map->add(InputTypeNames::image, ImageInputType::create);
92     map->add(InputTypeNames::month, MonthInputType::create);
93     map->add(InputTypeNames::number, NumberInputType::create);
94     map->add(InputTypeNames::password, PasswordInputType::create);
95     map->add(InputTypeNames::radio, RadioInputType::create);
96     map->add(InputTypeNames::range, RangeInputType::create);
97     map->add(InputTypeNames::reset, ResetInputType::create);
98     map->add(InputTypeNames::search, SearchInputType::create);
99     map->add(InputTypeNames::submit, SubmitInputType::create);
100     map->add(InputTypeNames::tel, TelephoneInputType::create);
101     map->add(InputTypeNames::time, TimeInputType::create);
102     map->add(InputTypeNames::url, URLInputType::create);
103     if (RuntimeEnabledFeatures::inputTypeWeekEnabled())
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 PassRefPtr<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 PassRefPtr<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 
canChangeFromAnotherType(const AtomicString & normalizedTypeName)136 bool InputType::canChangeFromAnotherType(const AtomicString& normalizedTypeName)
137 {
138     // Don't allow the type to be changed to file after the first type change.
139     // In other engines this might mean a JavaScript programmer could set a text
140     // field's value to something like /etc/passwd and then change it to a file
141     // input. I don't think this would actually occur in Blink, but this rule
142     // still may be important for compatibility.
143     return normalizedTypeName != InputTypeNames::file;
144 }
145 
~InputType()146 InputType::~InputType()
147 {
148 }
149 
themeSupportsDataListUI(InputType * type)150 bool InputType::themeSupportsDataListUI(InputType* type)
151 {
152     return RenderTheme::theme().supportsDataListUI(type->formControlType());
153 }
154 
isTextField() const155 bool InputType::isTextField() const
156 {
157     return false;
158 }
159 
isTextType() const160 bool InputType::isTextType() const
161 {
162     return false;
163 }
164 
isRangeControl() const165 bool InputType::isRangeControl() const
166 {
167     return false;
168 }
169 
shouldSaveAndRestoreFormControlState() const170 bool InputType::shouldSaveAndRestoreFormControlState() const
171 {
172     return true;
173 }
174 
saveFormControlState() const175 FormControlState InputType::saveFormControlState() const
176 {
177     String currentValue = element().value();
178     if (currentValue == element().defaultValue())
179         return FormControlState();
180     return FormControlState(currentValue);
181 }
182 
restoreFormControlState(const FormControlState & state)183 void InputType::restoreFormControlState(const FormControlState& state)
184 {
185     element().setValue(state[0]);
186 }
187 
isFormDataAppendable() const188 bool InputType::isFormDataAppendable() const
189 {
190     // There is no form data unless there's a name for non-image types.
191     return !element().name().isEmpty();
192 }
193 
appendFormData(FormDataList & encoding,bool) const194 bool InputType::appendFormData(FormDataList& encoding, bool) const
195 {
196     // Always successful.
197     encoding.appendData(element().name(), element().value());
198     return true;
199 }
200 
resultForDialogSubmit() const201 String InputType::resultForDialogSubmit() const
202 {
203     return element().fastGetAttribute(valueAttr);
204 }
205 
valueAsDate() const206 double InputType::valueAsDate() const
207 {
208     return DateComponents::invalidMilliseconds();
209 }
210 
setValueAsDate(double,ExceptionState & exceptionState) const211 void InputType::setValueAsDate(double, ExceptionState& exceptionState) const
212 {
213     exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
214 }
215 
valueAsDouble() const216 double InputType::valueAsDouble() const
217 {
218     return numeric_limits<double>::quiet_NaN();
219 }
220 
setValueAsDouble(double doubleValue,TextFieldEventBehavior eventBehavior,ExceptionState & exceptionState) const221 void InputType::setValueAsDouble(double doubleValue, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState) const
222 {
223     setValueAsDecimal(Decimal::fromDouble(doubleValue), eventBehavior, exceptionState);
224 }
225 
setValueAsDecimal(const Decimal &,TextFieldEventBehavior,ExceptionState & exceptionState) const226 void InputType::setValueAsDecimal(const Decimal&, TextFieldEventBehavior, ExceptionState& exceptionState) const
227 {
228     exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
229 }
230 
supportsValidation() const231 bool InputType::supportsValidation() const
232 {
233     return true;
234 }
235 
typeMismatchFor(const String &) const236 bool InputType::typeMismatchFor(const String&) const
237 {
238     return false;
239 }
240 
typeMismatch() const241 bool InputType::typeMismatch() const
242 {
243     return false;
244 }
245 
supportsRequired() const246 bool InputType::supportsRequired() const
247 {
248     // Almost all validatable types support @required.
249     return supportsValidation();
250 }
251 
valueMissing(const String &) const252 bool InputType::valueMissing(const String&) const
253 {
254     return false;
255 }
256 
hasBadInput() const257 bool InputType::hasBadInput() const
258 {
259     return false;
260 }
261 
patternMismatch(const String &) const262 bool InputType::patternMismatch(const String&) const
263 {
264     return false;
265 }
266 
rangeUnderflow(const String & value) const267 bool InputType::rangeUnderflow(const String& value) const
268 {
269     if (!isSteppable())
270         return false;
271 
272     const Decimal numericValue = parseToNumberOrNaN(value);
273     if (!numericValue.isFinite())
274         return false;
275 
276     return numericValue < createStepRange(RejectAny).minimum();
277 }
278 
rangeOverflow(const String & value) const279 bool InputType::rangeOverflow(const String& value) const
280 {
281     if (!isSteppable())
282         return false;
283 
284     const Decimal numericValue = parseToNumberOrNaN(value);
285     if (!numericValue.isFinite())
286         return false;
287 
288     return numericValue > createStepRange(RejectAny).maximum();
289 }
290 
defaultValueForStepUp() const291 Decimal InputType::defaultValueForStepUp() const
292 {
293     return 0;
294 }
295 
minimum() const296 double InputType::minimum() const
297 {
298     return createStepRange(RejectAny).minimum().toDouble();
299 }
300 
maximum() const301 double InputType::maximum() const
302 {
303     return createStepRange(RejectAny).maximum().toDouble();
304 }
305 
isInRange(const String & value) const306 bool InputType::isInRange(const String& value) const
307 {
308     if (!isSteppable())
309         return false;
310 
311     const Decimal numericValue = parseToNumberOrNaN(value);
312     if (!numericValue.isFinite())
313         return true;
314 
315     StepRange stepRange(createStepRange(RejectAny));
316     return numericValue >= stepRange.minimum() && numericValue <= stepRange.maximum();
317 }
318 
isOutOfRange(const String & value) const319 bool InputType::isOutOfRange(const String& value) const
320 {
321     if (!isSteppable())
322         return false;
323 
324     const Decimal numericValue = parseToNumberOrNaN(value);
325     if (!numericValue.isFinite())
326         return true;
327 
328     StepRange stepRange(createStepRange(RejectAny));
329     return numericValue < stepRange.minimum() || numericValue > stepRange.maximum();
330 }
331 
stepMismatch(const String & value) const332 bool InputType::stepMismatch(const String& value) const
333 {
334     if (!isSteppable())
335         return false;
336 
337     const Decimal numericValue = parseToNumberOrNaN(value);
338     if (!numericValue.isFinite())
339         return false;
340 
341     return createStepRange(RejectAny).stepMismatch(numericValue);
342 }
343 
badInputText() const344 String InputType::badInputText() const
345 {
346     ASSERT_NOT_REACHED();
347     return locale().queryString(WebLocalizedString::ValidationTypeMismatch);
348 }
349 
rangeOverflowText(const Decimal &) const350 String InputType::rangeOverflowText(const Decimal&) const
351 {
352     ASSERT_NOT_REACHED();
353     return String();
354 }
355 
rangeUnderflowText(const Decimal &) const356 String InputType::rangeUnderflowText(const Decimal&) const
357 {
358     ASSERT_NOT_REACHED();
359     return String();
360 }
361 
typeMismatchText() const362 String InputType::typeMismatchText() const
363 {
364     return locale().queryString(WebLocalizedString::ValidationTypeMismatch);
365 }
366 
valueMissingText() const367 String InputType::valueMissingText() const
368 {
369     return locale().queryString(WebLocalizedString::ValidationValueMissing);
370 }
371 
validationMessage() const372 String InputType::validationMessage() const
373 {
374     const String value = element().value();
375 
376     // The order of the following checks is meaningful. e.g. We'd like to show the
377     // badInput message even if the control has other validation errors.
378     if (hasBadInput())
379         return badInputText();
380 
381     if (valueMissing(value))
382         return valueMissingText();
383 
384     if (typeMismatch())
385         return typeMismatchText();
386 
387     if (patternMismatch(value))
388         return locale().queryString(WebLocalizedString::ValidationPatternMismatch);
389 
390     if (element().tooLong())
391         return locale().validationMessageTooLongText(value.length(), element().maxLength());
392 
393     if (!isSteppable())
394         return emptyString();
395 
396     const Decimal numericValue = parseToNumberOrNaN(value);
397     if (!numericValue.isFinite())
398         return emptyString();
399 
400     StepRange stepRange(createStepRange(RejectAny));
401 
402     if (numericValue < stepRange.minimum())
403         return rangeUnderflowText(stepRange.minimum());
404 
405     if (numericValue > stepRange.maximum())
406         return rangeOverflowText(stepRange.maximum());
407 
408     if (stepRange.stepMismatch(numericValue)) {
409         ASSERT(stepRange.hasStep());
410         Decimal candidate1 = stepRange.clampValue(numericValue);
411         String localizedCandidate1 = localizeValue(serialize(candidate1));
412         Decimal candidate2 = candidate1 < numericValue ? candidate1 + stepRange.step() : candidate1 - stepRange.step();
413         if (!candidate2.isFinite() || candidate2 < stepRange.minimum() || candidate2 > stepRange.maximum())
414             return locale().queryString(WebLocalizedString::ValidationStepMismatchCloseToLimit, localizedCandidate1);
415         String localizedCandidate2 = localizeValue(serialize(candidate2));
416         if (candidate1 < candidate2)
417             return locale().queryString(WebLocalizedString::ValidationStepMismatch, localizedCandidate1, localizedCandidate2);
418         return locale().queryString(WebLocalizedString::ValidationStepMismatch, localizedCandidate2, localizedCandidate1);
419     }
420 
421     return emptyString();
422 }
423 
shouldSubmitImplicitly(Event * event)424 bool InputType::shouldSubmitImplicitly(Event* event)
425 {
426     return event->isKeyboardEvent() && event->type() == EventTypeNames::keypress && toKeyboardEvent(event)->charCode() == '\r';
427 }
428 
parseToNumber(const String &,const Decimal & defaultValue) const429 Decimal InputType::parseToNumber(const String&, const Decimal& defaultValue) const
430 {
431     ASSERT_NOT_REACHED();
432     return defaultValue;
433 }
434 
parseToNumberOrNaN(const String & string) const435 Decimal InputType::parseToNumberOrNaN(const String& string) const
436 {
437     return parseToNumber(string, Decimal::nan());
438 }
439 
parseToDateComponents(const String &,DateComponents *) const440 bool InputType::parseToDateComponents(const String&, DateComponents*) const
441 {
442     ASSERT_NOT_REACHED();
443     return false;
444 }
445 
serialize(const Decimal &) const446 String InputType::serialize(const Decimal&) const
447 {
448     ASSERT_NOT_REACHED();
449     return String();
450 }
451 
dispatchSimulatedClickIfActive(KeyboardEvent * event) const452 void InputType::dispatchSimulatedClickIfActive(KeyboardEvent* event) const
453 {
454     if (element().active())
455         element().dispatchSimulatedClick(event);
456     event->setDefaultHandled();
457 }
458 
chrome() const459 Chrome* InputType::chrome() const
460 {
461     if (Page* page = element().document().page())
462         return &page->chrome();
463     return 0;
464 }
465 
locale() const466 Locale& InputType::locale() const
467 {
468     return element().locale();
469 }
470 
canSetStringValue() const471 bool InputType::canSetStringValue() const
472 {
473     return true;
474 }
475 
hasCustomFocusLogic() const476 bool InputType::hasCustomFocusLogic() const
477 {
478     return true;
479 }
480 
isKeyboardFocusable() const481 bool InputType::isKeyboardFocusable() const
482 {
483     return element().isFocusable();
484 }
485 
shouldShowFocusRingOnMouseFocus() const486 bool InputType::shouldShowFocusRingOnMouseFocus() const
487 {
488     return false;
489 }
490 
shouldUseInputMethod() const491 bool InputType::shouldUseInputMethod() const
492 {
493     return false;
494 }
495 
enableSecureTextInput()496 void InputType::enableSecureTextInput()
497 {
498 }
499 
disableSecureTextInput()500 void InputType::disableSecureTextInput()
501 {
502 }
503 
accessKeyAction(bool)504 void InputType::accessKeyAction(bool)
505 {
506     element().focus(false);
507 }
508 
countUsage()509 void InputType::countUsage()
510 {
511 }
512 
shouldRespectAlignAttribute()513 bool InputType::shouldRespectAlignAttribute()
514 {
515     return false;
516 }
517 
sanitizeValueInResponseToMinOrMaxAttributeChange()518 void InputType::sanitizeValueInResponseToMinOrMaxAttributeChange()
519 {
520 }
521 
canBeSuccessfulSubmitButton()522 bool InputType::canBeSuccessfulSubmitButton()
523 {
524     return false;
525 }
526 
rendererIsNeeded()527 bool InputType::rendererIsNeeded()
528 {
529     return true;
530 }
531 
files()532 FileList* InputType::files()
533 {
534     return 0;
535 }
536 
setFiles(PassRefPtr<FileList>)537 void InputType::setFiles(PassRefPtr<FileList>)
538 {
539 }
540 
getTypeSpecificValue(String &)541 bool InputType::getTypeSpecificValue(String&)
542 {
543     return false;
544 }
545 
fallbackValue() const546 String InputType::fallbackValue() const
547 {
548     return String();
549 }
550 
defaultValue() const551 String InputType::defaultValue() const
552 {
553     return String();
554 }
555 
canSetSuggestedValue()556 bool InputType::canSetSuggestedValue()
557 {
558     return false;
559 }
560 
shouldSendChangeEventAfterCheckedChanged()561 bool InputType::shouldSendChangeEventAfterCheckedChanged()
562 {
563     return true;
564 }
565 
storesValueSeparateFromAttribute()566 bool InputType::storesValueSeparateFromAttribute()
567 {
568     return true;
569 }
570 
setValue(const String & sanitizedValue,bool valueChanged,TextFieldEventBehavior eventBehavior)571 void InputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
572 {
573     element().setValueInternal(sanitizedValue, eventBehavior);
574     element().setNeedsStyleRecalc();
575     if (!valueChanged)
576         return;
577     switch (eventBehavior) {
578     case DispatchChangeEvent:
579         element().dispatchFormControlChangeEvent();
580         break;
581     case DispatchInputAndChangeEvent:
582         element().dispatchFormControlInputEvent();
583         element().dispatchFormControlChangeEvent();
584         break;
585     case DispatchNoEvent:
586         break;
587     }
588 }
589 
canSetValue(const String &)590 bool InputType::canSetValue(const String&)
591 {
592     return true;
593 }
594 
localizeValue(const String & proposedValue) const595 String InputType::localizeValue(const String& proposedValue) const
596 {
597     return proposedValue;
598 }
599 
visibleValue() const600 String InputType::visibleValue() const
601 {
602     return element().value();
603 }
604 
sanitizeValue(const String & proposedValue) const605 String InputType::sanitizeValue(const String& proposedValue) const
606 {
607     return proposedValue;
608 }
609 
receiveDroppedFiles(const DragData *)610 bool InputType::receiveDroppedFiles(const DragData*)
611 {
612     ASSERT_NOT_REACHED();
613     return false;
614 }
615 
droppedFileSystemId()616 String InputType::droppedFileSystemId()
617 {
618     ASSERT_NOT_REACHED();
619     return String();
620 }
621 
shouldResetOnDocumentActivation()622 bool InputType::shouldResetOnDocumentActivation()
623 {
624     return false;
625 }
626 
shouldRespectListAttribute()627 bool InputType::shouldRespectListAttribute()
628 {
629     return false;
630 }
631 
shouldRespectSpeechAttribute()632 bool InputType::shouldRespectSpeechAttribute()
633 {
634     return false;
635 }
636 
isTextButton() const637 bool InputType::isTextButton() const
638 {
639     return false;
640 }
641 
isRadioButton() const642 bool InputType::isRadioButton() const
643 {
644     return false;
645 }
646 
isSearchField() const647 bool InputType::isSearchField() const
648 {
649     return false;
650 }
651 
isHiddenType() const652 bool InputType::isHiddenType() const
653 {
654     return false;
655 }
656 
isPasswordField() const657 bool InputType::isPasswordField() const
658 {
659     return false;
660 }
661 
isCheckbox() const662 bool InputType::isCheckbox() const
663 {
664     return false;
665 }
666 
isEmailField() const667 bool InputType::isEmailField() const
668 {
669     return false;
670 }
671 
isFileUpload() const672 bool InputType::isFileUpload() const
673 {
674     return false;
675 }
676 
isImageButton() const677 bool InputType::isImageButton() const
678 {
679     return false;
680 }
681 
isInteractiveContent() const682 bool InputType::isInteractiveContent() const
683 {
684     return true;
685 }
686 
isNumberField() const687 bool InputType::isNumberField() const
688 {
689     return false;
690 }
691 
isSubmitButton() const692 bool InputType::isSubmitButton() const
693 {
694     return false;
695 }
696 
isTelephoneField() const697 bool InputType::isTelephoneField() const
698 {
699     return false;
700 }
701 
isURLField() const702 bool InputType::isURLField() const
703 {
704     return false;
705 }
706 
isDateField() const707 bool InputType::isDateField() const
708 {
709     return false;
710 }
711 
isDateTimeLocalField() const712 bool InputType::isDateTimeLocalField() const
713 {
714     return false;
715 }
716 
isMonthField() const717 bool InputType::isMonthField() const
718 {
719     return false;
720 }
721 
isTimeField() const722 bool InputType::isTimeField() const
723 {
724     return false;
725 }
726 
isWeekField() const727 bool InputType::isWeekField() const
728 {
729     return false;
730 }
731 
isEnumeratable()732 bool InputType::isEnumeratable()
733 {
734     return true;
735 }
736 
isCheckable()737 bool InputType::isCheckable()
738 {
739     return false;
740 }
741 
isSteppable() const742 bool InputType::isSteppable() const
743 {
744     return false;
745 }
746 
isColorControl() const747 bool InputType::isColorControl() const
748 {
749     return false;
750 }
751 
shouldRespectHeightAndWidthAttributes()752 bool InputType::shouldRespectHeightAndWidthAttributes()
753 {
754     return false;
755 }
756 
supportsPlaceholder() const757 bool InputType::supportsPlaceholder() const
758 {
759     return false;
760 }
761 
supportsReadOnly() const762 bool InputType::supportsReadOnly() const
763 {
764     return false;
765 }
766 
updatePlaceholderText()767 void InputType::updatePlaceholderText()
768 {
769 }
770 
defaultToolTip() const771 String InputType::defaultToolTip() const
772 {
773     return String();
774 }
775 
findClosestTickMarkValue(const Decimal &)776 Decimal InputType::findClosestTickMarkValue(const Decimal&)
777 {
778     ASSERT_NOT_REACHED();
779     return Decimal::nan();
780 }
781 
handleDOMActivateEvent(Event *)782 void InputType::handleDOMActivateEvent(Event*)
783 {
784 }
785 
supportsIndeterminateAppearance() const786 bool InputType::supportsIndeterminateAppearance() const
787 {
788     return false;
789 }
790 
supportsInputModeAttribute() const791 bool InputType::supportsInputModeAttribute() const
792 {
793     return false;
794 }
795 
supportsSelectionAPI() const796 bool InputType::supportsSelectionAPI() const
797 {
798     return false;
799 }
800 
height() const801 unsigned InputType::height() const
802 {
803     return 0;
804 }
805 
width() const806 unsigned InputType::width() const
807 {
808     return 0;
809 }
810 
applyStep(int count,AnyStepHandling anyStepHandling,TextFieldEventBehavior eventBehavior,ExceptionState & exceptionState)811 void InputType::applyStep(int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState)
812 {
813     StepRange stepRange(createStepRange(anyStepHandling));
814     if (!stepRange.hasStep()) {
815         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
816         return;
817     }
818 
819     const Decimal current = parseToNumberOrNaN(element().value());
820     if (!current.isFinite()) {
821         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
822         return;
823     }
824     Decimal newValue = current + stepRange.step() * count;
825     if (!newValue.isFinite()) {
826         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
827         return;
828     }
829 
830     const Decimal acceptableErrorValue = stepRange.acceptableError();
831     if (newValue - stepRange.minimum() < -acceptableErrorValue) {
832         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
833         return;
834     }
835     if (newValue < stepRange.minimum())
836         newValue = stepRange.minimum();
837 
838     const AtomicString& stepString = element().fastGetAttribute(stepAttr);
839     if (!equalIgnoringCase(stepString, "any"))
840         newValue = stepRange.alignValueForStep(current, newValue);
841 
842     if (newValue - stepRange.maximum() > acceptableErrorValue) {
843         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
844         return;
845     }
846     if (newValue > stepRange.maximum())
847         newValue = stepRange.maximum();
848 
849     setValueAsDecimal(newValue, eventBehavior, exceptionState);
850 
851     if (AXObjectCache* cache = element().document().existingAXObjectCache())
852         cache->postNotification(&element(), AXObjectCache::AXValueChanged, true);
853 }
854 
getAllowedValueStep(Decimal * step) const855 bool InputType::getAllowedValueStep(Decimal* step) const
856 {
857     StepRange stepRange(createStepRange(RejectAny));
858     *step = stepRange.step();
859     return stepRange.hasStep();
860 }
861 
createStepRange(AnyStepHandling) const862 StepRange InputType::createStepRange(AnyStepHandling) const
863 {
864     ASSERT_NOT_REACHED();
865     return StepRange();
866 }
867 
stepUp(int n,ExceptionState & exceptionState)868 void InputType::stepUp(int n, ExceptionState& exceptionState)
869 {
870     if (!isSteppable()) {
871         exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
872         return;
873     }
874     applyStep(n, RejectAny, DispatchNoEvent, exceptionState);
875 }
876 
stepUpFromRenderer(int n)877 void InputType::stepUpFromRenderer(int n)
878 {
879     // The differences from stepUp()/stepDown():
880     //
881     // Difference 1: the current value
882     // If the current value is not a number, including empty, the current value is assumed as 0.
883     //   * If 0 is in-range, and matches to step value
884     //     - The value should be the +step if n > 0
885     //     - The value should be the -step if n < 0
886     //     If -step or +step is out of range, new value should be 0.
887     //   * If 0 is smaller than the minimum value
888     //     - The value should be the minimum value for any n
889     //   * If 0 is larger than the maximum value
890     //     - The value should be the maximum value for any n
891     //   * If 0 is in-range, but not matched to step value
892     //     - The value should be the larger matched value nearest to 0 if n > 0
893     //       e.g. <input type=number min=-100 step=3> -> 2
894     //     - The value should be the smaler matched value nearest to 0 if n < 0
895     //       e.g. <input type=number min=-100 step=3> -> -1
896     //   As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time".
897     //   As for datetime type, the current value is assumed as "the current date/time in UTC".
898     // If the current value is smaller than the minimum value:
899     //  - The value should be the minimum value if n > 0
900     //  - Nothing should happen if n < 0
901     // If the current value is larger than the maximum value:
902     //  - The value should be the maximum value if n < 0
903     //  - Nothing should happen if n > 0
904     //
905     // Difference 2: clamping steps
906     // If the current value is not matched to step value:
907     // - The value should be the larger matched value nearest to 0 if n > 0
908     //   e.g. <input type=number value=3 min=-100 step=3> -> 5
909     // - The value should be the smaler matched value nearest to 0 if n < 0
910     //   e.g. <input type=number value=3 min=-100 step=3> -> 2
911     //
912     // n is assumed as -n if step < 0.
913 
914     ASSERT(isSteppable());
915     if (!isSteppable())
916         return;
917     ASSERT(n);
918     if (!n)
919         return;
920 
921     StepRange stepRange(createStepRange(AnyIsDefaultStep));
922 
923     // FIXME: Not any changes after stepping, even if it is an invalid value, may be better.
924     // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => "foo")
925     if (!stepRange.hasStep())
926         return;
927 
928     EventQueueScope scope;
929     const Decimal step = stepRange.step();
930 
931     int sign;
932     if (step > 0)
933         sign = n;
934     else if (step < 0)
935         sign = -n;
936     else
937         sign = 0;
938 
939     String currentStringValue = element().value();
940     Decimal current = parseToNumberOrNaN(currentStringValue);
941     if (!current.isFinite()) {
942         current = defaultValueForStepUp();
943         const Decimal nextDiff = step * n;
944         if (current < stepRange.minimum() - nextDiff)
945             current = stepRange.minimum() - nextDiff;
946         if (current > stepRange.maximum() - nextDiff)
947             current = stepRange.maximum() - nextDiff;
948         setValueAsDecimal(current, DispatchNoEvent, IGNORE_EXCEPTION);
949     }
950     if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum())) {
951         setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchInputAndChangeEvent, IGNORE_EXCEPTION);
952     } else {
953         if (stepMismatch(element().value())) {
954             ASSERT(!step.isZero());
955             const Decimal base = stepRange.stepBase();
956             Decimal newValue;
957             if (sign < 0)
958                 newValue = base + ((current - base) / step).floor() * step;
959             else if (sign > 0)
960                 newValue = base + ((current - base) / step).ceiling() * step;
961             else
962                 newValue = current;
963 
964             if (newValue < stepRange.minimum())
965                 newValue = stepRange.minimum();
966             if (newValue > stepRange.maximum())
967                 newValue = stepRange.maximum();
968 
969             setValueAsDecimal(newValue, n == 1 || n == -1 ? DispatchInputAndChangeEvent : DispatchNoEvent, IGNORE_EXCEPTION);
970             if (n > 1)
971                 applyStep(n - 1, AnyIsDefaultStep, DispatchInputAndChangeEvent, IGNORE_EXCEPTION);
972             else if (n < -1)
973                 applyStep(n + 1, AnyIsDefaultStep, DispatchInputAndChangeEvent, IGNORE_EXCEPTION);
974         } else {
975             applyStep(n, AnyIsDefaultStep, DispatchInputAndChangeEvent, IGNORE_EXCEPTION);
976         }
977     }
978 }
979 
countUsageIfVisible(UseCounter::Feature feature) const980 void InputType::countUsageIfVisible(UseCounter::Feature feature) const
981 {
982     if (RenderStyle* style = element().renderStyle()) {
983         if (style->visibility() != HIDDEN)
984             UseCounter::count(element().document(), feature);
985     }
986 }
987 
findStepBase(const Decimal & defaultValue) const988 Decimal InputType::findStepBase(const Decimal& defaultValue) const
989 {
990     Decimal stepBase = parseToNumber(element().fastGetAttribute(minAttr), Decimal::nan());
991     if (!stepBase.isFinite())
992         stepBase = parseToNumber(element().fastGetAttribute(valueAttr), defaultValue);
993     return stepBase;
994 }
995 
createStepRange(AnyStepHandling anyStepHandling,const Decimal & stepBaseDefault,const Decimal & minimumDefault,const Decimal & maximumDefault,const StepRange::StepDescription & stepDescription) const996 StepRange InputType::createStepRange(AnyStepHandling anyStepHandling, const Decimal& stepBaseDefault, const Decimal& minimumDefault, const Decimal& maximumDefault, const StepRange::StepDescription& stepDescription) const
997 {
998     const Decimal stepBase = findStepBase(stepBaseDefault);
999     const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), minimumDefault);
1000     const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), maximumDefault);
1001     const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
1002     return StepRange(stepBase, minimum, maximum, step, stepDescription);
1003 }
1004 
1005 } // namespace WebCore
1006