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