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