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/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 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::isTextType() const 146 { 147 return false; 148 } 149 150 bool InputType::isRangeControl() const 151 { 152 return false; 153 } 154 155 bool InputType::shouldSaveAndRestoreFormControlState() const 156 { 157 return true; 158 } 159 160 FormControlState InputType::saveFormControlState() const 161 { 162 String currentValue = element().value(); 163 if (currentValue == element().defaultValue()) 164 return FormControlState(); 165 return FormControlState(currentValue); 166 } 167 168 void InputType::restoreFormControlState(const FormControlState& state) 169 { 170 element().setValue(state[0]); 171 } 172 173 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 179 bool InputType::appendFormData(FormDataList& encoding, bool) const 180 { 181 // Always successful. 182 encoding.appendData(element().name(), element().value()); 183 return true; 184 } 185 186 String InputType::resultForDialogSubmit() const 187 { 188 return element().fastGetAttribute(valueAttr); 189 } 190 191 double InputType::valueAsDate() const 192 { 193 return DateComponents::invalidMilliseconds(); 194 } 195 196 void InputType::setValueAsDate(double, ExceptionState& exceptionState) const 197 { 198 exceptionState.throwDOMException(InvalidStateError, "This input element does not support Date values."); 199 } 200 201 double InputType::valueAsDouble() const 202 { 203 return std::numeric_limits<double>::quiet_NaN(); 204 } 205 206 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 211 void InputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior, ExceptionState&) const 212 { 213 element().setValue(serialize(newValue), eventBehavior); 214 } 215 216 bool InputType::supportsValidation() const 217 { 218 return true; 219 } 220 221 bool InputType::typeMismatchFor(const String&) const 222 { 223 return false; 224 } 225 226 bool InputType::typeMismatch() const 227 { 228 return false; 229 } 230 231 bool InputType::supportsRequired() const 232 { 233 // Almost all validatable types support @required. 234 return supportsValidation(); 235 } 236 237 bool InputType::valueMissing(const String&) const 238 { 239 return false; 240 } 241 242 bool InputType::hasBadInput() const 243 { 244 return false; 245 } 246 247 bool InputType::patternMismatch(const String&) const 248 { 249 return false; 250 } 251 252 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 264 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 276 Decimal InputType::defaultValueForStepUp() const 277 { 278 return 0; 279 } 280 281 double InputType::minimum() const 282 { 283 return createStepRange(RejectAny).minimum().toDouble(); 284 } 285 286 double InputType::maximum() const 287 { 288 return createStepRange(RejectAny).maximum().toDouble(); 289 } 290 291 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 304 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 317 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 329 String InputType::badInputText() const 330 { 331 ASSERT_NOT_REACHED(); 332 return locale().queryString(WebLocalizedString::ValidationTypeMismatch); 333 } 334 335 String InputType::rangeOverflowText(const Decimal&) const 336 { 337 ASSERT_NOT_REACHED(); 338 return String(); 339 } 340 341 String InputType::rangeUnderflowText(const Decimal&) const 342 { 343 ASSERT_NOT_REACHED(); 344 return String(); 345 } 346 347 String InputType::typeMismatchText() const 348 { 349 return locale().queryString(WebLocalizedString::ValidationTypeMismatch); 350 } 351 352 String InputType::valueMissingText() const 353 { 354 return locale().queryString(WebLocalizedString::ValidationValueMissing); 355 } 356 357 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 409 bool InputType::shouldSubmitImplicitly(Event* event) 410 { 411 return event->isKeyboardEvent() && event->type() == EventTypeNames::keypress && toKeyboardEvent(event)->charCode() == '\r'; 412 } 413 414 Decimal InputType::parseToNumber(const String&, const Decimal& defaultValue) const 415 { 416 ASSERT_NOT_REACHED(); 417 return defaultValue; 418 } 419 420 Decimal InputType::parseToNumberOrNaN(const String& string) const 421 { 422 return parseToNumber(string, Decimal::nan()); 423 } 424 425 String InputType::serialize(const Decimal&) const 426 { 427 ASSERT_NOT_REACHED(); 428 return String(); 429 } 430 431 void InputType::dispatchSimulatedClickIfActive(KeyboardEvent* event) const 432 { 433 if (element().active()) 434 element().dispatchSimulatedClick(event); 435 event->setDefaultHandled(); 436 } 437 438 Chrome* InputType::chrome() const 439 { 440 if (FrameHost* host = element().document().frameHost()) 441 return &host->chrome(); 442 return 0; 443 } 444 445 Locale& InputType::locale() const 446 { 447 return element().locale(); 448 } 449 450 bool InputType::canSetStringValue() const 451 { 452 return true; 453 } 454 455 bool InputType::hasCustomFocusLogic() const 456 { 457 return true; 458 } 459 460 bool InputType::isKeyboardFocusable() const 461 { 462 return element().isFocusable(); 463 } 464 465 bool InputType::shouldShowFocusRingOnMouseFocus() const 466 { 467 return false; 468 } 469 470 bool InputType::shouldUseInputMethod() const 471 { 472 return false; 473 } 474 475 void InputType::enableSecureTextInput() 476 { 477 } 478 479 void InputType::disableSecureTextInput() 480 { 481 } 482 483 void InputType::accessKeyAction(bool) 484 { 485 element().focus(false); 486 } 487 488 void InputType::countUsage() 489 { 490 } 491 492 bool InputType::shouldRespectAlignAttribute() 493 { 494 return false; 495 } 496 497 void InputType::sanitizeValueInResponseToMinOrMaxAttributeChange() 498 { 499 } 500 501 bool InputType::canBeSuccessfulSubmitButton() 502 { 503 return false; 504 } 505 506 bool InputType::rendererIsNeeded() 507 { 508 return true; 509 } 510 511 FileList* InputType::files() 512 { 513 return 0; 514 } 515 516 void InputType::setFiles(PassRefPtrWillBeRawPtr<FileList>) 517 { 518 } 519 520 bool InputType::getTypeSpecificValue(String&) 521 { 522 return false; 523 } 524 525 String InputType::fallbackValue() const 526 { 527 return String(); 528 } 529 530 String InputType::defaultValue() const 531 { 532 return String(); 533 } 534 535 bool InputType::canSetSuggestedValue() 536 { 537 return false; 538 } 539 540 bool InputType::shouldSendChangeEventAfterCheckedChanged() 541 { 542 return true; 543 } 544 545 bool InputType::storesValueSeparateFromAttribute() 546 { 547 return true; 548 } 549 550 bool InputType::shouldDispatchFormControlChangeEvent(String& oldValue, String& newValue) 551 { 552 return !equalIgnoringNullity(oldValue, newValue); 553 } 554 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 574 bool InputType::canSetValue(const String&) 575 { 576 return true; 577 } 578 579 String InputType::localizeValue(const String& proposedValue) const 580 { 581 return proposedValue; 582 } 583 584 String InputType::visibleValue() const 585 { 586 return element().value(); 587 } 588 589 String InputType::sanitizeValue(const String& proposedValue) const 590 { 591 return proposedValue; 592 } 593 594 bool InputType::receiveDroppedFiles(const DragData*) 595 { 596 ASSERT_NOT_REACHED(); 597 return false; 598 } 599 600 String InputType::droppedFileSystemId() 601 { 602 ASSERT_NOT_REACHED(); 603 return String(); 604 } 605 606 bool InputType::shouldRespectListAttribute() 607 { 608 return false; 609 } 610 611 bool InputType::shouldRespectSpeechAttribute() 612 { 613 return false; 614 } 615 616 bool InputType::isTextButton() const 617 { 618 return false; 619 } 620 621 bool InputType::isRadioButton() const 622 { 623 return false; 624 } 625 626 bool InputType::isSearchField() const 627 { 628 return false; 629 } 630 631 bool InputType::isHiddenType() const 632 { 633 return false; 634 } 635 636 bool InputType::isPasswordField() const 637 { 638 return false; 639 } 640 641 bool InputType::isCheckbox() const 642 { 643 return false; 644 } 645 646 bool InputType::isEmailField() const 647 { 648 return false; 649 } 650 651 bool InputType::isFileUpload() const 652 { 653 return false; 654 } 655 656 bool InputType::isImageButton() const 657 { 658 return false; 659 } 660 661 bool InputType::isInteractiveContent() const 662 { 663 return true; 664 } 665 666 bool InputType::isNumberField() const 667 { 668 return false; 669 } 670 671 bool InputType::isTelephoneField() const 672 { 673 return false; 674 } 675 676 bool InputType::isURLField() const 677 { 678 return false; 679 } 680 681 bool InputType::isDateField() const 682 { 683 return false; 684 } 685 686 bool InputType::isDateTimeLocalField() const 687 { 688 return false; 689 } 690 691 bool InputType::isMonthField() const 692 { 693 return false; 694 } 695 696 bool InputType::isTimeField() const 697 { 698 return false; 699 } 700 701 bool InputType::isWeekField() const 702 { 703 return false; 704 } 705 706 bool InputType::isEnumeratable() 707 { 708 return true; 709 } 710 711 bool InputType::isCheckable() 712 { 713 return false; 714 } 715 716 bool InputType::isSteppable() const 717 { 718 return false; 719 } 720 721 bool InputType::isColorControl() const 722 { 723 return false; 724 } 725 726 bool InputType::shouldRespectHeightAndWidthAttributes() 727 { 728 return false; 729 } 730 731 bool InputType::supportsPlaceholder() const 732 { 733 return false; 734 } 735 736 bool InputType::supportsReadOnly() const 737 { 738 return false; 739 } 740 741 String InputType::defaultToolTip() const 742 { 743 return String(); 744 } 745 746 Decimal InputType::findClosestTickMarkValue(const Decimal&) 747 { 748 ASSERT_NOT_REACHED(); 749 return Decimal::nan(); 750 } 751 752 void InputType::handleDOMActivateEvent(Event*) 753 { 754 } 755 756 bool InputType::hasLegalLinkAttribute(const QualifiedName&) const 757 { 758 return false; 759 } 760 761 const QualifiedName& InputType::subResourceAttributeName() const 762 { 763 return QualifiedName::null(); 764 } 765 766 bool InputType::supportsIndeterminateAppearance() const 767 { 768 return false; 769 } 770 771 bool InputType::supportsInputModeAttribute() const 772 { 773 return false; 774 } 775 776 bool InputType::supportsSelectionAPI() const 777 { 778 return false; 779 } 780 781 unsigned InputType::height() const 782 { 783 return 0; 784 } 785 786 unsigned InputType::width() const 787 { 788 return 0; 789 } 790 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 853 bool InputType::getAllowedValueStep(Decimal* step) const 854 { 855 StepRange stepRange(createStepRange(RejectAny)); 856 *step = stepRange.step(); 857 return stepRange.hasStep(); 858 } 859 860 StepRange InputType::createStepRange(AnyStepHandling) const 861 { 862 ASSERT_NOT_REACHED(); 863 return StepRange(); 864 } 865 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 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 948 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 956 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 964 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