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