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 "InputType.h" 30 31#include "AXObjectCache.h" 32#include "BeforeTextInsertedEvent.h" 33#include "ButtonInputType.h" 34#include "CheckboxInputType.h" 35#include "ColorInputType.h" 36#include "DateComponents.h" 37#include "DateInputType.h" 38#include "DateTimeInputType.h" 39#include "DateTimeLocalInputType.h" 40#include "ElementShadow.h" 41#include "EmailInputType.h" 42#include "ExceptionCode.h" 43#include "ExceptionCodePlaceholder.h" 44#include "FileInputType.h" 45#include "FileList.h" 46#include "FormController.h" 47#include "FormDataList.h" 48#include "HTMLFormElement.h" 49#include "HTMLInputElement.h" 50#include "HTMLNames.h" 51#include "HTMLParserIdioms.h" 52#include "HiddenInputType.h" 53#include "ImageInputType.h" 54#include "InputTypeNames.h" 55#include "KeyboardEvent.h" 56#include "LocalizedStrings.h" 57#include "MonthInputType.h" 58#include "NodeRenderStyle.h" 59#include "NumberInputType.h" 60#include "Page.h" 61#include "PasswordInputType.h" 62#include "RadioInputType.h" 63#include "RangeInputType.h" 64#include "RegularExpression.h" 65#include "RenderObject.h" 66#include "RenderTheme.h" 67#include "ResetInputType.h" 68#include "RuntimeEnabledFeatures.h" 69#include "ScopedEventQueue.h" 70#include "SearchInputType.h" 71#include "ShadowRoot.h" 72#include "SubmitInputType.h" 73#include "TelephoneInputType.h" 74#include "TextBreakIterator.h" 75#include "TextInputType.h" 76#include "TimeInputType.h" 77#include "URLInputType.h" 78#include "WeekInputType.h" 79#include <limits> 80#include <wtf/Assertions.h> 81#include <wtf/HashMap.h> 82#include <wtf/text/StringHash.h> 83 84namespace WebCore { 85 86using namespace HTMLNames; 87using namespace std; 88 89typedef PassOwnPtr<InputType> (*InputTypeFactoryFunction)(HTMLInputElement*); 90typedef HashMap<AtomicString, InputTypeFactoryFunction, CaseFoldingHash> InputTypeFactoryMap; 91 92static PassOwnPtr<InputTypeFactoryMap> createInputTypeFactoryMap() 93{ 94 OwnPtr<InputTypeFactoryMap> map = adoptPtr(new InputTypeFactoryMap); 95 map->add(InputTypeNames::button(), ButtonInputType::create); 96 map->add(InputTypeNames::checkbox(), CheckboxInputType::create); 97#if ENABLE(INPUT_TYPE_COLOR) 98 map->add(InputTypeNames::color(), ColorInputType::create); 99#endif 100#if ENABLE(INPUT_TYPE_DATE) 101 if (RuntimeEnabledFeatures::inputTypeDateEnabled()) 102 map->add(InputTypeNames::date(), DateInputType::create); 103#endif 104#if ENABLE(INPUT_TYPE_DATETIME_INCOMPLETE) 105 if (RuntimeEnabledFeatures::inputTypeDateTimeEnabled()) 106 map->add(InputTypeNames::datetime(), DateTimeInputType::create); 107#endif 108#if ENABLE(INPUT_TYPE_DATETIMELOCAL) 109 if (RuntimeEnabledFeatures::inputTypeDateTimeLocalEnabled()) 110 map->add(InputTypeNames::datetimelocal(), DateTimeLocalInputType::create); 111#endif 112 map->add(InputTypeNames::email(), EmailInputType::create); 113 map->add(InputTypeNames::file(), FileInputType::create); 114 map->add(InputTypeNames::hidden(), HiddenInputType::create); 115 map->add(InputTypeNames::image(), ImageInputType::create); 116#if ENABLE(INPUT_TYPE_MONTH) 117 if (RuntimeEnabledFeatures::inputTypeMonthEnabled()) 118 map->add(InputTypeNames::month(), MonthInputType::create); 119#endif 120 map->add(InputTypeNames::number(), NumberInputType::create); 121 map->add(InputTypeNames::password(), PasswordInputType::create); 122 map->add(InputTypeNames::radio(), RadioInputType::create); 123 map->add(InputTypeNames::range(), RangeInputType::create); 124 map->add(InputTypeNames::reset(), ResetInputType::create); 125 map->add(InputTypeNames::search(), SearchInputType::create); 126 map->add(InputTypeNames::submit(), SubmitInputType::create); 127 map->add(InputTypeNames::telephone(), TelephoneInputType::create); 128#if ENABLE(INPUT_TYPE_TIME) 129 if (RuntimeEnabledFeatures::inputTypeTimeEnabled()) 130 map->add(InputTypeNames::time(), TimeInputType::create); 131#endif 132 map->add(InputTypeNames::url(), URLInputType::create); 133#if ENABLE(INPUT_TYPE_WEEK) 134 if (RuntimeEnabledFeatures::inputTypeWeekEnabled()) 135 map->add(InputTypeNames::week(), WeekInputType::create); 136#endif 137 // No need to register "text" because it is the default type. 138 return map.release(); 139} 140 141PassOwnPtr<InputType> InputType::create(HTMLInputElement* element, const AtomicString& typeName) 142{ 143 static const InputTypeFactoryMap* factoryMap = createInputTypeFactoryMap().leakPtr(); 144 PassOwnPtr<InputType> (*factory)(HTMLInputElement*) = typeName.isEmpty() ? 0 : factoryMap->get(typeName); 145 if (!factory) 146 factory = TextInputType::create; 147 return factory(element); 148} 149 150PassOwnPtr<InputType> InputType::createText(HTMLInputElement* element) 151{ 152 return TextInputType::create(element); 153} 154 155InputType::~InputType() 156{ 157} 158 159bool InputType::themeSupportsDataListUI(InputType* type) 160{ 161 Document* document = type->element()->document(); 162 RefPtr<RenderTheme> theme = document->page() ? document->page()->theme() : RenderTheme::defaultTheme(); 163 return theme->supportsDataListUI(type->formControlType()); 164} 165 166bool InputType::isTextField() const 167{ 168 return false; 169} 170 171bool InputType::isTextType() const 172{ 173 return false; 174} 175 176bool InputType::isRangeControl() const 177{ 178 return false; 179} 180 181bool InputType::shouldSaveAndRestoreFormControlState() const 182{ 183 return true; 184} 185 186FormControlState InputType::saveFormControlState() const 187{ 188 String currentValue = element()->value(); 189 if (currentValue == element()->defaultValue()) 190 return FormControlState(); 191 return FormControlState(currentValue); 192} 193 194void InputType::restoreFormControlState(const FormControlState& state) 195{ 196 element()->setValue(state[0]); 197} 198 199bool InputType::isFormDataAppendable() const 200{ 201 // There is no form data unless there's a name for non-image types. 202 return !element()->name().isEmpty(); 203} 204 205bool InputType::appendFormData(FormDataList& encoding, bool) const 206{ 207 // Always successful. 208 encoding.appendData(element()->name(), element()->value()); 209 return true; 210} 211 212double InputType::valueAsDate() const 213{ 214 return DateComponents::invalidMilliseconds(); 215} 216 217void InputType::setValueAsDate(double, ExceptionCode& ec) const 218{ 219 ec = INVALID_STATE_ERR; 220} 221 222double InputType::valueAsDouble() const 223{ 224 return numeric_limits<double>::quiet_NaN(); 225} 226 227void InputType::setValueAsDouble(double doubleValue, TextFieldEventBehavior eventBehavior, ExceptionCode& ec) const 228{ 229 setValueAsDecimal(Decimal::fromDouble(doubleValue), eventBehavior, ec); 230} 231 232void InputType::setValueAsDecimal(const Decimal&, TextFieldEventBehavior, ExceptionCode& ec) const 233{ 234 ec = INVALID_STATE_ERR; 235} 236 237bool InputType::supportsValidation() const 238{ 239 return true; 240} 241 242bool InputType::typeMismatchFor(const String&) const 243{ 244 return false; 245} 246 247bool InputType::typeMismatch() const 248{ 249 return false; 250} 251 252bool InputType::supportsRequired() const 253{ 254 // Almost all validatable types support @required. 255 return supportsValidation(); 256} 257 258bool InputType::valueMissing(const String&) const 259{ 260 return false; 261} 262 263bool InputType::hasBadInput() const 264{ 265 return false; 266} 267 268bool InputType::patternMismatch(const String&) const 269{ 270 return false; 271} 272 273bool InputType::rangeUnderflow(const String& value) const 274{ 275 if (!isSteppable()) 276 return false; 277 278 const Decimal numericValue = parseToNumberOrNaN(value); 279 if (!numericValue.isFinite()) 280 return false; 281 282 return numericValue < createStepRange(RejectAny).minimum(); 283} 284 285bool InputType::rangeOverflow(const String& value) const 286{ 287 if (!isSteppable()) 288 return false; 289 290 const Decimal numericValue = parseToNumberOrNaN(value); 291 if (!numericValue.isFinite()) 292 return false; 293 294 return numericValue > createStepRange(RejectAny).maximum(); 295} 296 297Decimal InputType::defaultValueForStepUp() const 298{ 299 return 0; 300} 301 302double InputType::minimum() const 303{ 304 return createStepRange(RejectAny).minimum().toDouble(); 305} 306 307double InputType::maximum() const 308{ 309 return createStepRange(RejectAny).maximum().toDouble(); 310} 311 312bool InputType::sizeShouldIncludeDecoration(int, int& preferredSize) const 313{ 314 preferredSize = element()->size(); 315 return false; 316} 317 318bool InputType::isInRange(const String& value) const 319{ 320 if (!isSteppable()) 321 return false; 322 323 const Decimal numericValue = parseToNumberOrNaN(value); 324 if (!numericValue.isFinite()) 325 return true; 326 327 StepRange stepRange(createStepRange(RejectAny)); 328 return numericValue >= stepRange.minimum() && numericValue <= stepRange.maximum(); 329} 330 331bool InputType::isOutOfRange(const String& value) const 332{ 333 if (!isSteppable()) 334 return false; 335 336 const Decimal numericValue = parseToNumberOrNaN(value); 337 if (!numericValue.isFinite()) 338 return true; 339 340 StepRange stepRange(createStepRange(RejectAny)); 341 return numericValue < stepRange.minimum() || numericValue > stepRange.maximum(); 342} 343 344bool InputType::stepMismatch(const String& value) const 345{ 346 if (!isSteppable()) 347 return false; 348 349 const Decimal numericValue = parseToNumberOrNaN(value); 350 if (!numericValue.isFinite()) 351 return false; 352 353 return createStepRange(RejectAny).stepMismatch(numericValue); 354} 355 356String InputType::badInputText() const 357{ 358 ASSERT_NOT_REACHED(); 359 return validationMessageTypeMismatchText(); 360} 361 362String InputType::typeMismatchText() const 363{ 364 return validationMessageTypeMismatchText(); 365} 366 367String InputType::valueMissingText() const 368{ 369 return validationMessageValueMissingText(); 370} 371 372String 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 validationMessagePatternMismatchText(); 389 390 if (element()->tooLong()) 391 return validationMessageTooLongText(numGraphemeClusters(value), 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 validationMessageRangeUnderflowText(serialize(stepRange.minimum())); 404 405 if (numericValue > stepRange.maximum()) 406 return validationMessageRangeOverflowText(serialize(stepRange.maximum())); 407 408 if (stepRange.stepMismatch(numericValue)) { 409 const String stepString = stepRange.hasStep() ? serializeForNumberType(stepRange.step() / stepRange.stepScaleFactor()) : emptyString(); 410 return validationMessageStepMismatchText(serialize(stepRange.stepBase()), stepString); 411 } 412 413 return emptyString(); 414} 415 416void InputType::handleClickEvent(MouseEvent*) 417{ 418} 419 420void InputType::handleMouseDownEvent(MouseEvent*) 421{ 422} 423 424void InputType::handleDOMActivateEvent(Event*) 425{ 426} 427 428void InputType::handleKeydownEvent(KeyboardEvent*) 429{ 430} 431 432void InputType::handleKeypressEvent(KeyboardEvent*) 433{ 434} 435 436void InputType::handleKeyupEvent(KeyboardEvent*) 437{ 438} 439 440void InputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent*) 441{ 442} 443 444#if ENABLE(TOUCH_EVENTS) 445void InputType::handleTouchEvent(TouchEvent*) 446{ 447} 448#endif 449 450void InputType::forwardEvent(Event*) 451{ 452} 453 454bool InputType::shouldSubmitImplicitly(Event* event) 455{ 456 return event->isKeyboardEvent() && event->type() == eventNames().keypressEvent && static_cast<KeyboardEvent*>(event)->charCode() == '\r'; 457} 458 459PassRefPtr<HTMLFormElement> InputType::formForSubmission() const 460{ 461 return element()->form(); 462} 463 464RenderObject* InputType::createRenderer(RenderArena*, RenderStyle* style) const 465{ 466 return RenderObject::createObject(element(), style); 467} 468 469void InputType::blur() 470{ 471 element()->defaultBlur(); 472} 473 474void InputType::createShadowSubtree() 475{ 476} 477 478void InputType::destroyShadowSubtree() 479{ 480 ShadowRoot* root = element()->userAgentShadowRoot(); 481 if (!root) 482 return; 483 484 root->removeChildren(); 485} 486 487Decimal InputType::parseToNumber(const String&, const Decimal& defaultValue) const 488{ 489 ASSERT_NOT_REACHED(); 490 return defaultValue; 491} 492 493Decimal InputType::parseToNumberOrNaN(const String& string) const 494{ 495 return parseToNumber(string, Decimal::nan()); 496} 497 498bool InputType::parseToDateComponents(const String&, DateComponents*) const 499{ 500 ASSERT_NOT_REACHED(); 501 return false; 502} 503 504String InputType::serialize(const Decimal&) const 505{ 506 ASSERT_NOT_REACHED(); 507 return String(); 508} 509 510void InputType::dispatchSimulatedClickIfActive(KeyboardEvent* event) const 511{ 512 if (element()->active()) 513 element()->dispatchSimulatedClick(event); 514 event->setDefaultHandled(); 515} 516 517Chrome* InputType::chrome() const 518{ 519 if (Page* page = element()->document()->page()) 520 return &page->chrome(); 521 return 0; 522} 523 524bool InputType::canSetStringValue() const 525{ 526 return true; 527} 528 529bool InputType::hasCustomFocusLogic() const 530{ 531 return true; 532} 533 534bool InputType::isKeyboardFocusable(KeyboardEvent* event) const 535{ 536 return element()->isTextFormControlKeyboardFocusable(event); 537} 538 539bool InputType::isMouseFocusable() const 540{ 541 return element()->isTextFormControlMouseFocusable(); 542} 543 544bool InputType::shouldUseInputMethod() const 545{ 546 return false; 547} 548 549void InputType::handleFocusEvent(Node*, FocusDirection) 550{ 551} 552 553void InputType::handleBlurEvent() 554{ 555} 556 557void InputType::accessKeyAction(bool) 558{ 559 element()->focus(false); 560} 561 562void InputType::addSearchResult() 563{ 564} 565 566void InputType::attach() 567{ 568} 569 570void InputType::detach() 571{ 572} 573 574void InputType::altAttributeChanged() 575{ 576} 577 578void InputType::srcAttributeChanged() 579{ 580} 581 582bool InputType::shouldRespectAlignAttribute() 583{ 584 return false; 585} 586 587bool InputType::canChangeFromAnotherType() const 588{ 589 return true; 590} 591 592void InputType::minOrMaxAttributeChanged() 593{ 594} 595 596void InputType::stepAttributeChanged() 597{ 598} 599 600bool InputType::canBeSuccessfulSubmitButton() 601{ 602 return false; 603} 604 605HTMLElement* InputType::placeholderElement() const 606{ 607 return 0; 608} 609 610bool InputType::rendererIsNeeded() 611{ 612 return true; 613} 614 615FileList* InputType::files() 616{ 617 return 0; 618} 619 620void InputType::setFiles(PassRefPtr<FileList>) 621{ 622} 623 624bool InputType::getTypeSpecificValue(String&) 625{ 626 return false; 627} 628 629String InputType::fallbackValue() const 630{ 631 return String(); 632} 633 634String InputType::defaultValue() const 635{ 636 return String(); 637} 638 639bool InputType::canSetSuggestedValue() 640{ 641 return false; 642} 643 644bool InputType::shouldSendChangeEventAfterCheckedChanged() 645{ 646 return true; 647} 648 649bool InputType::storesValueSeparateFromAttribute() 650{ 651 return true; 652} 653 654void InputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior) 655{ 656 element()->setValueInternal(sanitizedValue, eventBehavior); 657 element()->setNeedsStyleRecalc(); 658 if (!valueChanged) 659 return; 660 switch (eventBehavior) { 661 case DispatchChangeEvent: 662 element()->dispatchFormControlChangeEvent(); 663 break; 664 case DispatchInputAndChangeEvent: 665 element()->dispatchFormControlInputEvent(); 666 element()->dispatchFormControlChangeEvent(); 667 break; 668 case DispatchNoEvent: 669 break; 670 } 671} 672 673bool InputType::canSetValue(const String&) 674{ 675 return true; 676} 677 678PassOwnPtr<ClickHandlingState> InputType::willDispatchClick() 679{ 680 return nullptr; 681} 682 683void InputType::didDispatchClick(Event*, const ClickHandlingState&) 684{ 685} 686 687String InputType::localizeValue(const String& proposedValue) const 688{ 689 return proposedValue; 690} 691 692String InputType::visibleValue() const 693{ 694 return element()->value(); 695} 696 697String InputType::sanitizeValue(const String& proposedValue) const 698{ 699 return proposedValue; 700} 701 702bool InputType::receiveDroppedFiles(const DragData*) 703{ 704 ASSERT_NOT_REACHED(); 705 return false; 706} 707 708#if ENABLE(FILE_SYSTEM) 709String InputType::droppedFileSystemId() 710{ 711 ASSERT_NOT_REACHED(); 712 return String(); 713} 714#endif 715 716Icon* InputType::icon() const 717{ 718 ASSERT_NOT_REACHED(); 719 return 0; 720} 721 722bool InputType::shouldResetOnDocumentActivation() 723{ 724 return false; 725} 726 727bool InputType::shouldRespectListAttribute() 728{ 729 return false; 730} 731 732bool InputType::shouldRespectSpeechAttribute() 733{ 734 return false; 735} 736 737bool InputType::isTextButton() const 738{ 739 return false; 740} 741 742bool InputType::isRadioButton() const 743{ 744 return false; 745} 746 747bool InputType::isSearchField() const 748{ 749 return false; 750} 751 752bool InputType::isHiddenType() const 753{ 754 return false; 755} 756 757bool InputType::isPasswordField() const 758{ 759 return false; 760} 761 762bool InputType::isCheckbox() const 763{ 764 return false; 765} 766 767bool InputType::isEmailField() const 768{ 769 return false; 770} 771 772bool InputType::isFileUpload() const 773{ 774 return false; 775} 776 777bool InputType::isImageButton() const 778{ 779 return false; 780} 781 782bool InputType::supportLabels() const 783{ 784 return true; 785} 786 787bool InputType::isNumberField() const 788{ 789 return false; 790} 791 792bool InputType::isSubmitButton() const 793{ 794 return false; 795} 796 797bool InputType::isTelephoneField() const 798{ 799 return false; 800} 801 802bool InputType::isURLField() const 803{ 804 return false; 805} 806 807bool InputType::isDateField() const 808{ 809 return false; 810} 811 812bool InputType::isDateTimeField() const 813{ 814 return false; 815} 816 817bool InputType::isDateTimeLocalField() const 818{ 819 return false; 820} 821 822bool InputType::isMonthField() const 823{ 824 return false; 825} 826 827bool InputType::isTimeField() const 828{ 829 return false; 830} 831 832bool InputType::isWeekField() const 833{ 834 return false; 835} 836 837bool InputType::isEnumeratable() 838{ 839 return true; 840} 841 842bool InputType::isCheckable() 843{ 844 return false; 845} 846 847bool InputType::isSteppable() const 848{ 849 return false; 850} 851 852#if ENABLE(INPUT_TYPE_COLOR) 853bool InputType::isColorControl() const 854{ 855 return false; 856} 857#endif 858 859bool InputType::shouldRespectHeightAndWidthAttributes() 860{ 861 return false; 862} 863 864bool InputType::supportsPlaceholder() const 865{ 866 return false; 867} 868 869bool InputType::supportsReadOnly() const 870{ 871 return false; 872} 873 874void InputType::updateInnerTextValue() 875{ 876} 877 878void InputType::updatePlaceholderText() 879{ 880} 881 882void InputType::attributeChanged() 883{ 884} 885 886void InputType::multipleAttributeChanged() 887{ 888} 889 890void InputType::disabledAttributeChanged() 891{ 892} 893 894void InputType::readonlyAttributeChanged() 895{ 896} 897 898void InputType::requiredAttributeChanged() 899{ 900} 901 902void InputType::valueAttributeChanged() 903{ 904} 905 906void InputType::subtreeHasChanged() 907{ 908 ASSERT_NOT_REACHED(); 909} 910 911#if ENABLE(TOUCH_EVENTS) 912bool InputType::hasTouchEventHandler() const 913{ 914 return false; 915} 916#endif 917 918String InputType::defaultToolTip() const 919{ 920 return String(); 921} 922 923#if ENABLE(DATALIST_ELEMENT) 924void InputType::listAttributeTargetChanged() 925{ 926} 927 928Decimal InputType::findClosestTickMarkValue(const Decimal&) 929{ 930 ASSERT_NOT_REACHED(); 931 return Decimal::nan(); 932} 933#endif 934 935void InputType::updateClearButtonVisibility() 936{ 937} 938 939bool InputType::supportsIndeterminateAppearance() const 940{ 941 return false; 942} 943 944bool InputType::supportsSelectionAPI() const 945{ 946 return false; 947} 948 949unsigned InputType::height() const 950{ 951 return 0; 952} 953 954unsigned InputType::width() const 955{ 956 return 0; 957} 958 959void InputType::applyStep(int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior, ExceptionCode& ec) 960{ 961 StepRange stepRange(createStepRange(anyStepHandling)); 962 if (!stepRange.hasStep()) { 963 ec = INVALID_STATE_ERR; 964 return; 965 } 966 967 const Decimal current = parseToNumberOrNaN(element()->value()); 968 if (!current.isFinite()) { 969 ec = INVALID_STATE_ERR; 970 return; 971 } 972 Decimal newValue = current + stepRange.step() * count; 973 if (!newValue.isFinite()) { 974 ec = INVALID_STATE_ERR; 975 return; 976 } 977 978 const Decimal acceptableErrorValue = stepRange.acceptableError(); 979 if (newValue - stepRange.minimum() < -acceptableErrorValue) { 980 ec = INVALID_STATE_ERR; 981 return; 982 } 983 if (newValue < stepRange.minimum()) 984 newValue = stepRange.minimum(); 985 986 const AtomicString& stepString = element()->fastGetAttribute(stepAttr); 987 if (!equalIgnoringCase(stepString, "any")) 988 newValue = stepRange.alignValueForStep(current, newValue); 989 990 if (newValue - stepRange.maximum() > acceptableErrorValue) { 991 ec = INVALID_STATE_ERR; 992 return; 993 } 994 if (newValue > stepRange.maximum()) 995 newValue = stepRange.maximum(); 996 997 setValueAsDecimal(newValue, eventBehavior, ec); 998 999 if (AXObjectCache* cache = element()->document()->existingAXObjectCache()) 1000 cache->postNotification(element(), AXObjectCache::AXValueChanged, true); 1001} 1002 1003bool InputType::getAllowedValueStep(Decimal* step) const 1004{ 1005 StepRange stepRange(createStepRange(RejectAny)); 1006 *step = stepRange.step(); 1007 return stepRange.hasStep(); 1008} 1009 1010StepRange InputType::createStepRange(AnyStepHandling) const 1011{ 1012 ASSERT_NOT_REACHED(); 1013 return StepRange(); 1014} 1015 1016void InputType::stepUp(int n, ExceptionCode& ec) 1017{ 1018 if (!isSteppable()) { 1019 ec = INVALID_STATE_ERR; 1020 return; 1021 } 1022 applyStep(n, RejectAny, DispatchNoEvent, ec); 1023} 1024 1025void InputType::stepUpFromRenderer(int n) 1026{ 1027 // The differences from stepUp()/stepDown(): 1028 // 1029 // Difference 1: the current value 1030 // If the current value is not a number, including empty, the current value is assumed as 0. 1031 // * If 0 is in-range, and matches to step value 1032 // - The value should be the +step if n > 0 1033 // - The value should be the -step if n < 0 1034 // If -step or +step is out of range, new value should be 0. 1035 // * If 0 is smaller than the minimum value 1036 // - The value should be the minimum value for any n 1037 // * If 0 is larger than the maximum value 1038 // - The value should be the maximum value for any n 1039 // * If 0 is in-range, but not matched to step value 1040 // - The value should be the larger matched value nearest to 0 if n > 0 1041 // e.g. <input type=number min=-100 step=3> -> 2 1042 // - The value should be the smaler matched value nearest to 0 if n < 0 1043 // e.g. <input type=number min=-100 step=3> -> -1 1044 // As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time". 1045 // As for datetime type, the current value is assumed as "the current date/time in UTC". 1046 // If the current value is smaller than the minimum value: 1047 // - The value should be the minimum value if n > 0 1048 // - Nothing should happen if n < 0 1049 // If the current value is larger than the maximum value: 1050 // - The value should be the maximum value if n < 0 1051 // - Nothing should happen if n > 0 1052 // 1053 // Difference 2: clamping steps 1054 // If the current value is not matched to step value: 1055 // - The value should be the larger matched value nearest to 0 if n > 0 1056 // e.g. <input type=number value=3 min=-100 step=3> -> 5 1057 // - The value should be the smaler matched value nearest to 0 if n < 0 1058 // e.g. <input type=number value=3 min=-100 step=3> -> 2 1059 // 1060 // n is assumed as -n if step < 0. 1061 1062 ASSERT(isSteppable()); 1063 if (!isSteppable()) 1064 return; 1065 ASSERT(n); 1066 if (!n) 1067 return; 1068 1069 StepRange stepRange(createStepRange(AnyIsDefaultStep)); 1070 1071 // FIXME: Not any changes after stepping, even if it is an invalid value, may be better. 1072 // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => "foo") 1073 if (!stepRange.hasStep()) 1074 return; 1075 1076 EventQueueScope scope; 1077 const Decimal step = stepRange.step(); 1078 1079 int sign; 1080 if (step > 0) 1081 sign = n; 1082 else if (step < 0) 1083 sign = -n; 1084 else 1085 sign = 0; 1086 1087 String currentStringValue = element()->value(); 1088 Decimal current = parseToNumberOrNaN(currentStringValue); 1089 if (!current.isFinite()) { 1090 current = defaultValueForStepUp(); 1091 const Decimal nextDiff = step * n; 1092 if (current < stepRange.minimum() - nextDiff) 1093 current = stepRange.minimum() - nextDiff; 1094 if (current > stepRange.maximum() - nextDiff) 1095 current = stepRange.maximum() - nextDiff; 1096 setValueAsDecimal(current, DispatchNoEvent, IGNORE_EXCEPTION); 1097 } 1098 if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum())) 1099 setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchInputAndChangeEvent, IGNORE_EXCEPTION); 1100 else { 1101 if (stepMismatch(element()->value())) { 1102 ASSERT(!step.isZero()); 1103 const Decimal base = stepRange.stepBase(); 1104 Decimal newValue; 1105 if (sign < 0) 1106 newValue = base + ((current - base) / step).floor() * step; 1107 else if (sign > 0) 1108 newValue = base + ((current - base) / step).ceiling() * step; 1109 else 1110 newValue = current; 1111 1112 if (newValue < stepRange.minimum()) 1113 newValue = stepRange.minimum(); 1114 if (newValue > stepRange.maximum()) 1115 newValue = stepRange.maximum(); 1116 1117 setValueAsDecimal(newValue, n == 1 || n == -1 ? DispatchInputAndChangeEvent : DispatchNoEvent, IGNORE_EXCEPTION); 1118 if (n > 1) 1119 applyStep(n - 1, AnyIsDefaultStep, DispatchInputAndChangeEvent, IGNORE_EXCEPTION); 1120 else if (n < -1) 1121 applyStep(n + 1, AnyIsDefaultStep, DispatchInputAndChangeEvent, IGNORE_EXCEPTION); 1122 } else 1123 applyStep(n, AnyIsDefaultStep, DispatchInputAndChangeEvent, IGNORE_EXCEPTION); 1124 } 1125} 1126 1127void InputType::observeFeatureIfVisible(FeatureObserver::Feature feature) const 1128{ 1129 if (RenderStyle* style = element()->renderStyle()) { 1130 if (style->visibility() != HIDDEN) 1131 FeatureObserver::observe(element()->document(), feature); 1132 } 1133} 1134 1135} // namespace WebCore 1136