1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2001 Dirk Mueller (mueller (at) kde.org) 5 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 6 * (C) 2006 Alexey Proskuryakov (ap (at) nypop.com) 7 * Copyright (C) 2007 Samuel Weinig (sam (at) 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 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 109 static const InputTypeFactoryMap* factoryMap() 110 { 111 static const InputTypeFactoryMap* factoryMap = createInputTypeFactoryMap().leakPtr(); 112 return factoryMap; 113 } 114 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 123 PassRefPtr<InputType> InputType::createText(HTMLInputElement& element) 124 { 125 return TextInputType::create(element); 126 } 127 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 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 146 InputType::~InputType() 147 { 148 } 149 150 bool InputType::themeSupportsDataListUI(InputType* type) 151 { 152 return RenderTheme::theme().supportsDataListUI(type->formControlType()); 153 } 154 155 bool InputType::isTextField() const 156 { 157 return false; 158 } 159 160 bool InputType::isTextType() const 161 { 162 return false; 163 } 164 165 bool InputType::isRangeControl() const 166 { 167 return false; 168 } 169 170 bool InputType::shouldSaveAndRestoreFormControlState() const 171 { 172 return true; 173 } 174 175 FormControlState InputType::saveFormControlState() const 176 { 177 String currentValue = element().value(); 178 if (currentValue == element().defaultValue()) 179 return FormControlState(); 180 return FormControlState(currentValue); 181 } 182 183 void InputType::restoreFormControlState(const FormControlState& state) 184 { 185 element().setValue(state[0]); 186 } 187 188 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 194 bool InputType::appendFormData(FormDataList& encoding, bool) const 195 { 196 // Always successful. 197 encoding.appendData(element().name(), element().value()); 198 return true; 199 } 200 201 String InputType::resultForDialogSubmit() const 202 { 203 return element().fastGetAttribute(valueAttr); 204 } 205 206 double InputType::valueAsDate() const 207 { 208 return DateComponents::invalidMilliseconds(); 209 } 210 211 void InputType::setValueAsDate(double, ExceptionState& exceptionState) const 212 { 213 exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError); 214 } 215 216 double InputType::valueAsDouble() const 217 { 218 return numeric_limits<double>::quiet_NaN(); 219 } 220 221 void InputType::setValueAsDouble(double doubleValue, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState) const 222 { 223 setValueAsDecimal(Decimal::fromDouble(doubleValue), eventBehavior, exceptionState); 224 } 225 226 void InputType::setValueAsDecimal(const Decimal&, TextFieldEventBehavior, ExceptionState& exceptionState) const 227 { 228 exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError); 229 } 230 231 bool InputType::supportsValidation() const 232 { 233 return true; 234 } 235 236 bool InputType::typeMismatchFor(const String&) const 237 { 238 return false; 239 } 240 241 bool InputType::typeMismatch() const 242 { 243 return false; 244 } 245 246 bool InputType::supportsRequired() const 247 { 248 // Almost all validatable types support @required. 249 return supportsValidation(); 250 } 251 252 bool InputType::valueMissing(const String&) const 253 { 254 return false; 255 } 256 257 bool InputType::hasBadInput() const 258 { 259 return false; 260 } 261 262 bool InputType::patternMismatch(const String&) const 263 { 264 return false; 265 } 266 267 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 279 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 291 Decimal InputType::defaultValueForStepUp() const 292 { 293 return 0; 294 } 295 296 double InputType::minimum() const 297 { 298 return createStepRange(RejectAny).minimum().toDouble(); 299 } 300 301 double InputType::maximum() const 302 { 303 return createStepRange(RejectAny).maximum().toDouble(); 304 } 305 306 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 319 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 332 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 344 String InputType::badInputText() const 345 { 346 ASSERT_NOT_REACHED(); 347 return locale().queryString(WebLocalizedString::ValidationTypeMismatch); 348 } 349 350 String InputType::rangeOverflowText(const Decimal&) const 351 { 352 ASSERT_NOT_REACHED(); 353 return String(); 354 } 355 356 String InputType::rangeUnderflowText(const Decimal&) const 357 { 358 ASSERT_NOT_REACHED(); 359 return String(); 360 } 361 362 String InputType::typeMismatchText() const 363 { 364 return locale().queryString(WebLocalizedString::ValidationTypeMismatch); 365 } 366 367 String InputType::valueMissingText() const 368 { 369 return locale().queryString(WebLocalizedString::ValidationValueMissing); 370 } 371 372 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 424 bool InputType::shouldSubmitImplicitly(Event* event) 425 { 426 return event->isKeyboardEvent() && event->type() == EventTypeNames::keypress && toKeyboardEvent(event)->charCode() == '\r'; 427 } 428 429 Decimal InputType::parseToNumber(const String&, const Decimal& defaultValue) const 430 { 431 ASSERT_NOT_REACHED(); 432 return defaultValue; 433 } 434 435 Decimal InputType::parseToNumberOrNaN(const String& string) const 436 { 437 return parseToNumber(string, Decimal::nan()); 438 } 439 440 bool InputType::parseToDateComponents(const String&, DateComponents*) const 441 { 442 ASSERT_NOT_REACHED(); 443 return false; 444 } 445 446 String InputType::serialize(const Decimal&) const 447 { 448 ASSERT_NOT_REACHED(); 449 return String(); 450 } 451 452 void InputType::dispatchSimulatedClickIfActive(KeyboardEvent* event) const 453 { 454 if (element().active()) 455 element().dispatchSimulatedClick(event); 456 event->setDefaultHandled(); 457 } 458 459 Chrome* InputType::chrome() const 460 { 461 if (Page* page = element().document().page()) 462 return &page->chrome(); 463 return 0; 464 } 465 466 Locale& InputType::locale() const 467 { 468 return element().locale(); 469 } 470 471 bool InputType::canSetStringValue() const 472 { 473 return true; 474 } 475 476 bool InputType::hasCustomFocusLogic() const 477 { 478 return true; 479 } 480 481 bool InputType::isKeyboardFocusable() const 482 { 483 return element().isFocusable(); 484 } 485 486 bool InputType::shouldShowFocusRingOnMouseFocus() const 487 { 488 return false; 489 } 490 491 bool InputType::shouldUseInputMethod() const 492 { 493 return false; 494 } 495 496 void InputType::enableSecureTextInput() 497 { 498 } 499 500 void InputType::disableSecureTextInput() 501 { 502 } 503 504 void InputType::accessKeyAction(bool) 505 { 506 element().focus(false); 507 } 508 509 void InputType::countUsage() 510 { 511 } 512 513 bool InputType::shouldRespectAlignAttribute() 514 { 515 return false; 516 } 517 518 void InputType::sanitizeValueInResponseToMinOrMaxAttributeChange() 519 { 520 } 521 522 bool InputType::canBeSuccessfulSubmitButton() 523 { 524 return false; 525 } 526 527 bool InputType::rendererIsNeeded() 528 { 529 return true; 530 } 531 532 FileList* InputType::files() 533 { 534 return 0; 535 } 536 537 void InputType::setFiles(PassRefPtr<FileList>) 538 { 539 } 540 541 bool InputType::getTypeSpecificValue(String&) 542 { 543 return false; 544 } 545 546 String InputType::fallbackValue() const 547 { 548 return String(); 549 } 550 551 String InputType::defaultValue() const 552 { 553 return String(); 554 } 555 556 bool InputType::canSetSuggestedValue() 557 { 558 return false; 559 } 560 561 bool InputType::shouldSendChangeEventAfterCheckedChanged() 562 { 563 return true; 564 } 565 566 bool InputType::storesValueSeparateFromAttribute() 567 { 568 return true; 569 } 570 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 590 bool InputType::canSetValue(const String&) 591 { 592 return true; 593 } 594 595 String InputType::localizeValue(const String& proposedValue) const 596 { 597 return proposedValue; 598 } 599 600 String InputType::visibleValue() const 601 { 602 return element().value(); 603 } 604 605 String InputType::sanitizeValue(const String& proposedValue) const 606 { 607 return proposedValue; 608 } 609 610 bool InputType::receiveDroppedFiles(const DragData*) 611 { 612 ASSERT_NOT_REACHED(); 613 return false; 614 } 615 616 String InputType::droppedFileSystemId() 617 { 618 ASSERT_NOT_REACHED(); 619 return String(); 620 } 621 622 bool InputType::shouldResetOnDocumentActivation() 623 { 624 return false; 625 } 626 627 bool InputType::shouldRespectListAttribute() 628 { 629 return false; 630 } 631 632 bool InputType::shouldRespectSpeechAttribute() 633 { 634 return false; 635 } 636 637 bool InputType::isTextButton() const 638 { 639 return false; 640 } 641 642 bool InputType::isRadioButton() const 643 { 644 return false; 645 } 646 647 bool InputType::isSearchField() const 648 { 649 return false; 650 } 651 652 bool InputType::isHiddenType() const 653 { 654 return false; 655 } 656 657 bool InputType::isPasswordField() const 658 { 659 return false; 660 } 661 662 bool InputType::isCheckbox() const 663 { 664 return false; 665 } 666 667 bool InputType::isEmailField() const 668 { 669 return false; 670 } 671 672 bool InputType::isFileUpload() const 673 { 674 return false; 675 } 676 677 bool InputType::isImageButton() const 678 { 679 return false; 680 } 681 682 bool InputType::isInteractiveContent() const 683 { 684 return true; 685 } 686 687 bool InputType::isNumberField() const 688 { 689 return false; 690 } 691 692 bool InputType::isSubmitButton() const 693 { 694 return false; 695 } 696 697 bool InputType::isTelephoneField() const 698 { 699 return false; 700 } 701 702 bool InputType::isURLField() const 703 { 704 return false; 705 } 706 707 bool InputType::isDateField() const 708 { 709 return false; 710 } 711 712 bool InputType::isDateTimeLocalField() const 713 { 714 return false; 715 } 716 717 bool InputType::isMonthField() const 718 { 719 return false; 720 } 721 722 bool InputType::isTimeField() const 723 { 724 return false; 725 } 726 727 bool InputType::isWeekField() const 728 { 729 return false; 730 } 731 732 bool InputType::isEnumeratable() 733 { 734 return true; 735 } 736 737 bool InputType::isCheckable() 738 { 739 return false; 740 } 741 742 bool InputType::isSteppable() const 743 { 744 return false; 745 } 746 747 bool InputType::isColorControl() const 748 { 749 return false; 750 } 751 752 bool InputType::shouldRespectHeightAndWidthAttributes() 753 { 754 return false; 755 } 756 757 bool InputType::supportsPlaceholder() const 758 { 759 return false; 760 } 761 762 bool InputType::supportsReadOnly() const 763 { 764 return false; 765 } 766 767 void InputType::updatePlaceholderText() 768 { 769 } 770 771 String InputType::defaultToolTip() const 772 { 773 return String(); 774 } 775 776 Decimal InputType::findClosestTickMarkValue(const Decimal&) 777 { 778 ASSERT_NOT_REACHED(); 779 return Decimal::nan(); 780 } 781 782 void InputType::handleDOMActivateEvent(Event*) 783 { 784 } 785 786 bool InputType::supportsIndeterminateAppearance() const 787 { 788 return false; 789 } 790 791 bool InputType::supportsInputModeAttribute() const 792 { 793 return false; 794 } 795 796 bool InputType::supportsSelectionAPI() const 797 { 798 return false; 799 } 800 801 unsigned InputType::height() const 802 { 803 return 0; 804 } 805 806 unsigned InputType::width() const 807 { 808 return 0; 809 } 810 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 855 bool InputType::getAllowedValueStep(Decimal* step) const 856 { 857 StepRange stepRange(createStepRange(RejectAny)); 858 *step = stepRange.step(); 859 return stepRange.hasStep(); 860 } 861 862 StepRange InputType::createStepRange(AnyStepHandling) const 863 { 864 ASSERT_NOT_REACHED(); 865 return StepRange(); 866 } 867 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 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 980 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 988 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 996 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