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) 2010 Google Inc. All rights reserved. 9 * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 10 * Copyright (C) 2012 Samsung Electronics. All rights reserved. 11 * 12 * This library is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU Library General Public 14 * License as published by the Free Software Foundation; either 15 * version 2 of the License, or (at your option) any later version. 16 * 17 * This library is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 * Library General Public License for more details. 21 * 22 * You should have received a copy of the GNU Library General Public License 23 * along with this library; see the file COPYING.LIB. If not, write to 24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 * Boston, MA 02110-1301, USA. 26 * 27 */ 28 29#include "config.h" 30#include "core/html/HTMLInputElement.h" 31 32#include "CSSPropertyNames.h" 33#include "HTMLNames.h" 34#include "RuntimeEnabledFeatures.h" 35#include "bindings/v8/ExceptionMessages.h" 36#include "bindings/v8/ExceptionState.h" 37#include "bindings/v8/ScriptEventListener.h" 38#include "core/accessibility/AXObjectCache.h" 39#include "core/dom/Document.h" 40#include "core/dom/ExceptionCode.h" 41#include "core/dom/IdTargetObserver.h" 42#include "core/dom/shadow/ElementShadow.h" 43#include "core/dom/shadow/InsertionPoint.h" 44#include "core/dom/shadow/ShadowRoot.h" 45#include "core/editing/FrameSelection.h" 46#include "core/editing/SpellChecker.h" 47#include "core/events/BeforeTextInsertedEvent.h" 48#include "core/events/KeyboardEvent.h" 49#include "core/events/MouseEvent.h" 50#include "core/events/ScopedEventQueue.h" 51#include "core/events/ThreadLocalEventNames.h" 52#include "core/events/TouchEvent.h" 53#include "core/fileapi/FileList.h" 54#include "core/frame/Frame.h" 55#include "core/frame/FrameView.h" 56#include "core/html/HTMLCollection.h" 57#include "core/html/HTMLDataListElement.h" 58#include "core/html/HTMLFormElement.h" 59#include "core/html/HTMLImageLoader.h" 60#include "core/html/HTMLOptionElement.h" 61#include "core/html/forms/ColorInputType.h" 62#include "core/html/forms/FileInputType.h" 63#include "core/html/forms/FormController.h" 64#include "core/html/forms/InputType.h" 65#include "core/html/forms/SearchInputType.h" 66#include "core/html/parser/HTMLParserIdioms.h" 67#include "core/html/shadow/ShadowElementNames.h" 68#include "core/frame/UseCounter.h" 69#include "core/page/Chrome.h" 70#include "core/page/ChromeClient.h" 71#include "core/page/Page.h" 72#include "core/rendering/RenderTextControlSingleLine.h" 73#include "core/rendering/RenderTheme.h" 74#include "platform/DateTimeChooser.h" 75#include "platform/Language.h" 76#include "platform/PlatformMouseEvent.h" 77#include "platform/text/PlatformLocale.h" 78#include "wtf/MathExtras.h" 79 80using namespace std; 81 82namespace WebCore { 83 84using namespace HTMLNames; 85 86class ListAttributeTargetObserver : IdTargetObserver { 87 WTF_MAKE_FAST_ALLOCATED; 88public: 89 static PassOwnPtr<ListAttributeTargetObserver> create(const AtomicString& id, HTMLInputElement*); 90 virtual void idTargetChanged() OVERRIDE; 91 92private: 93 ListAttributeTargetObserver(const AtomicString& id, HTMLInputElement*); 94 95 HTMLInputElement* m_element; 96}; 97 98// FIXME: According to HTML4, the length attribute's value can be arbitrarily 99// large. However, due to https://bugs.webkit.org/show_bug.cgi?id=14536 things 100// get rather sluggish when a text field has a larger number of characters than 101// this, even when just clicking in the text field. 102const int HTMLInputElement::maximumLength = 524288; 103const int defaultSize = 20; 104const int maxSavedResults = 256; 105 106HTMLInputElement::HTMLInputElement(Document& document, HTMLFormElement* form, bool createdByParser) 107 : HTMLTextFormControlElement(inputTag, document, form) 108 , m_size(defaultSize) 109 , m_maxLength(maximumLength) 110 , m_maxResults(-1) 111 , m_isChecked(false) 112 , m_reflectsCheckedAttribute(true) 113 , m_isIndeterminate(false) 114 , m_hasType(false) 115 , m_isActivatedSubmit(false) 116 , m_autocomplete(Uninitialized) 117 , m_hasNonEmptyList(false) 118 , m_stateRestored(false) 119 , m_parsingInProgress(createdByParser) 120 , m_valueAttributeWasUpdatedAfterParsing(false) 121 , m_canReceiveDroppedFiles(false) 122 , m_hasTouchEventHandler(false) 123 , m_inputType(InputType::createText(*this)) 124 , m_inputTypeView(m_inputType) 125{ 126#if ENABLE(INPUT_MULTIPLE_FIELDS_UI) 127 setHasCustomStyleCallbacks(); 128#endif 129 ScriptWrappable::init(this); 130} 131 132PassRefPtr<HTMLInputElement> HTMLInputElement::create(Document& document, HTMLFormElement* form, bool createdByParser) 133{ 134 RefPtr<HTMLInputElement> inputElement = adoptRef(new HTMLInputElement(document, form, createdByParser)); 135 inputElement->ensureUserAgentShadowRoot(); 136 return inputElement.release(); 137} 138 139HTMLImageLoader* HTMLInputElement::imageLoader() 140{ 141 if (!m_imageLoader) 142 m_imageLoader = adoptPtr(new HTMLImageLoader(this)); 143 return m_imageLoader.get(); 144} 145 146void HTMLInputElement::didAddUserAgentShadowRoot(ShadowRoot&) 147{ 148 m_inputTypeView->createShadowSubtree(); 149} 150 151void HTMLInputElement::didAddShadowRoot(ShadowRoot& root) 152{ 153 if (!root.isOldestAuthorShadowRoot()) 154 return; 155 m_inputTypeView->destroyShadowSubtree(); 156 m_inputTypeView = InputTypeView::create(*this); 157 lazyReattachIfAttached(); 158} 159 160HTMLInputElement::~HTMLInputElement() 161{ 162 // Need to remove form association while this is still an HTMLInputElement 163 // so that virtual functions are called correctly. 164 setForm(0); 165 // setForm(0) may register this to a document-level radio button group. 166 // We should unregister it to avoid accessing a deleted object. 167 if (isRadioButton()) 168 document().formController()->checkedRadioButtons().removeButton(this); 169 if (m_hasTouchEventHandler) 170 document().didRemoveEventTargetNode(this); 171} 172 173const AtomicString& HTMLInputElement::name() const 174{ 175 return m_name.isNull() ? emptyAtom : m_name; 176} 177 178Vector<FileChooserFileInfo> HTMLInputElement::filesFromFileInputFormControlState(const FormControlState& state) 179{ 180 return FileInputType::filesFromFormControlState(state); 181} 182 183HTMLElement* HTMLInputElement::passwordGeneratorButtonElement() const 184{ 185 return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementNames::passwordGenerator())); 186} 187 188bool HTMLInputElement::shouldAutocomplete() const 189{ 190 if (m_autocomplete != Uninitialized) 191 return m_autocomplete == On; 192 return HTMLTextFormControlElement::shouldAutocomplete(); 193} 194 195bool HTMLInputElement::isValidValue(const String& value) const 196{ 197 if (!m_inputType->canSetStringValue()) { 198 ASSERT_NOT_REACHED(); 199 return false; 200 } 201 return !m_inputType->typeMismatchFor(value) 202 && !m_inputType->stepMismatch(value) 203 && !m_inputType->rangeUnderflow(value) 204 && !m_inputType->rangeOverflow(value) 205 && !tooLong(value, IgnoreDirtyFlag) 206 && !m_inputType->patternMismatch(value) 207 && !m_inputType->valueMissing(value); 208} 209 210bool HTMLInputElement::tooLong() const 211{ 212 return willValidate() && tooLong(value(), CheckDirtyFlag); 213} 214 215bool HTMLInputElement::typeMismatch() const 216{ 217 return willValidate() && m_inputType->typeMismatch(); 218} 219 220bool HTMLInputElement::valueMissing() const 221{ 222 return willValidate() && m_inputType->valueMissing(value()); 223} 224 225bool HTMLInputElement::hasBadInput() const 226{ 227 return willValidate() && m_inputType->hasBadInput(); 228} 229 230bool HTMLInputElement::patternMismatch() const 231{ 232 return willValidate() && m_inputType->patternMismatch(value()); 233} 234 235bool HTMLInputElement::tooLong(const String& value, NeedsToCheckDirtyFlag check) const 236{ 237 // We use isTextType() instead of supportsMaxLength() because of the 238 // 'virtual' overhead. 239 if (!isTextType()) 240 return false; 241 int max = maxLength(); 242 if (max < 0) 243 return false; 244 if (check == CheckDirtyFlag) { 245 // Return false for the default value or a value set by a script even if 246 // it is longer than maxLength. 247 if (!hasDirtyValue() || !lastChangeWasUserEdit()) 248 return false; 249 } 250 return value.length() > static_cast<unsigned>(max); 251} 252 253bool HTMLInputElement::rangeUnderflow() const 254{ 255 return willValidate() && m_inputType->rangeUnderflow(value()); 256} 257 258bool HTMLInputElement::rangeOverflow() const 259{ 260 return willValidate() && m_inputType->rangeOverflow(value()); 261} 262 263String HTMLInputElement::validationMessage() const 264{ 265 if (!willValidate()) 266 return String(); 267 268 if (customError()) 269 return customValidationMessage(); 270 271 return m_inputType->validationMessage(); 272} 273 274double HTMLInputElement::minimum() const 275{ 276 return m_inputType->minimum(); 277} 278 279double HTMLInputElement::maximum() const 280{ 281 return m_inputType->maximum(); 282} 283 284bool HTMLInputElement::stepMismatch() const 285{ 286 return willValidate() && m_inputType->stepMismatch(value()); 287} 288 289bool HTMLInputElement::getAllowedValueStep(Decimal* step) const 290{ 291 return m_inputType->getAllowedValueStep(step); 292} 293 294StepRange HTMLInputElement::createStepRange(AnyStepHandling anyStepHandling) const 295{ 296 return m_inputType->createStepRange(anyStepHandling); 297} 298 299Decimal HTMLInputElement::findClosestTickMarkValue(const Decimal& value) 300{ 301 return m_inputType->findClosestTickMarkValue(value); 302} 303 304void HTMLInputElement::stepUp(int n, ExceptionState& exceptionState) 305{ 306 m_inputType->stepUp(n, exceptionState); 307} 308 309void HTMLInputElement::stepDown(int n, ExceptionState& exceptionState) 310{ 311 m_inputType->stepUp(-n, exceptionState); 312} 313 314void HTMLInputElement::blur() 315{ 316 m_inputTypeView->blur(); 317} 318 319void HTMLInputElement::defaultBlur() 320{ 321 HTMLTextFormControlElement::blur(); 322} 323 324bool HTMLInputElement::hasCustomFocusLogic() const 325{ 326 return m_inputTypeView->hasCustomFocusLogic(); 327} 328 329bool HTMLInputElement::isKeyboardFocusable() const 330{ 331 return m_inputType->isKeyboardFocusable(); 332} 333 334bool HTMLInputElement::shouldShowFocusRingOnMouseFocus() const 335{ 336 return m_inputType->shouldShowFocusRingOnMouseFocus(); 337} 338 339void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection) 340{ 341 if (isTextField()) { 342 if (!restorePreviousSelection || !hasCachedSelection()) 343 select(); 344 else 345 restoreCachedSelection(); 346 if (document().frame()) 347 document().frame()->selection().revealSelection(); 348 } else 349 HTMLTextFormControlElement::updateFocusAppearance(restorePreviousSelection); 350} 351 352void HTMLInputElement::beginEditing() 353{ 354 if (!isTextField()) 355 return; 356 357 if (Frame* frame = document().frame()) 358 frame->spellChecker().didBeginEditing(this); 359} 360 361void HTMLInputElement::endEditing() 362{ 363 if (!isTextField()) 364 return; 365 366 if (Frame* frame = document().frame()) { 367 frame->spellChecker().didEndEditingOnTextField(this); 368 if (Page* page = frame->page()) 369 page->chrome().client().didEndEditingOnTextField(*this); 370 } 371} 372 373bool HTMLInputElement::shouldUseInputMethod() 374{ 375 return m_inputType->shouldUseInputMethod(); 376} 377 378void HTMLInputElement::handleFocusEvent(Element* oldFocusedElement, FocusDirection direction) 379{ 380 m_inputTypeView->handleFocusEvent(oldFocusedElement, direction); 381 m_inputType->enableSecureTextInput(); 382} 383 384void HTMLInputElement::handleBlurEvent() 385{ 386 m_inputType->disableSecureTextInput(); 387 m_inputTypeView->handleBlurEvent(); 388} 389 390void HTMLInputElement::setType(const AtomicString& type) 391{ 392 // FIXME: This should just call setAttribute. No reason to handle the empty string specially. 393 // We should write a test case to show that setting to the empty string does not remove the 394 // attribute in other browsers and then fix this. Note that setting to null *does* remove 395 // the attribute and setAttribute implements that. 396 if (type.isEmpty()) 397 removeAttribute(typeAttr); 398 else 399 setAttribute(typeAttr, type); 400} 401 402void HTMLInputElement::updateType() 403{ 404 const AtomicString& newTypeName = InputType::normalizeTypeName(fastGetAttribute(typeAttr)); 405 bool hadType = m_hasType; 406 m_hasType = true; 407 if (m_inputType->formControlType() == newTypeName) 408 return; 409 410 if (hadType && !InputType::canChangeFromAnotherType(newTypeName)) { 411 // Set the attribute back to the old value. 412 // Useful in case we were called from inside parseAttribute. 413 setAttribute(typeAttr, type()); 414 return; 415 } 416 417 RefPtr<InputType> newType = InputType::create(*this, newTypeName); 418 removeFromRadioButtonGroup(); 419 420 bool didStoreValue = m_inputType->storesValueSeparateFromAttribute(); 421 bool didRespectHeightAndWidth = m_inputType->shouldRespectHeightAndWidthAttributes(); 422 423 m_inputTypeView->destroyShadowSubtree(); 424 lazyReattachIfAttached(); 425 426 m_inputType = newType.release(); 427 if (hasAuthorShadowRoot()) 428 m_inputTypeView = InputTypeView::create(*this); 429 else 430 m_inputTypeView = m_inputType; 431 m_inputTypeView->createShadowSubtree(); 432 433 bool hasTouchEventHandler = m_inputTypeView->hasTouchEventHandler(); 434 if (hasTouchEventHandler != m_hasTouchEventHandler) { 435 if (hasTouchEventHandler) 436 document().didAddTouchEventHandler(this); 437 else 438 document().didRemoveTouchEventHandler(this); 439 m_hasTouchEventHandler = hasTouchEventHandler; 440 } 441 442 setNeedsWillValidateCheck(); 443 444 bool willStoreValue = m_inputType->storesValueSeparateFromAttribute(); 445 446 if (didStoreValue && !willStoreValue && hasDirtyValue()) { 447 setAttribute(valueAttr, AtomicString(m_valueIfDirty)); 448 m_valueIfDirty = String(); 449 } 450 if (!didStoreValue && willStoreValue) { 451 AtomicString valueString = fastGetAttribute(valueAttr); 452 m_valueIfDirty = sanitizeValue(valueString); 453 } else 454 updateValueIfNeeded(); 455 456 setFormControlValueMatchesRenderer(false); 457 m_inputTypeView->updateView(); 458 459 if (didRespectHeightAndWidth != m_inputType->shouldRespectHeightAndWidthAttributes()) { 460 ASSERT(elementData()); 461 if (const Attribute* height = getAttributeItem(heightAttr)) 462 attributeChanged(heightAttr, height->value()); 463 if (const Attribute* width = getAttributeItem(widthAttr)) 464 attributeChanged(widthAttr, width->value()); 465 if (const Attribute* align = getAttributeItem(alignAttr)) 466 attributeChanged(alignAttr, align->value()); 467 } 468 469 if (document().focusedElement() == this) 470 document().updateFocusAppearanceSoon(true /* restore selection */); 471 472 setChangedSinceLastFormControlChangeEvent(false); 473 474 addToRadioButtonGroup(); 475 476 setNeedsValidityCheck(); 477 notifyFormStateChanged(); 478} 479 480void HTMLInputElement::subtreeHasChanged() 481{ 482 m_inputTypeView->subtreeHasChanged(); 483 // When typing in an input field, childrenChanged is not called, so we need to force the directionality check. 484 calculateAndAdjustDirectionality(); 485} 486 487const AtomicString& HTMLInputElement::formControlType() const 488{ 489 return m_inputType->formControlType(); 490} 491 492bool HTMLInputElement::shouldSaveAndRestoreFormControlState() const 493{ 494 if (!m_inputType->shouldSaveAndRestoreFormControlState()) 495 return false; 496 return HTMLTextFormControlElement::shouldSaveAndRestoreFormControlState(); 497} 498 499FormControlState HTMLInputElement::saveFormControlState() const 500{ 501 return m_inputType->saveFormControlState(); 502} 503 504void HTMLInputElement::restoreFormControlState(const FormControlState& state) 505{ 506 m_inputType->restoreFormControlState(state); 507 m_stateRestored = true; 508} 509 510bool HTMLInputElement::canStartSelection() const 511{ 512 if (!isTextField()) 513 return false; 514 return HTMLTextFormControlElement::canStartSelection(); 515} 516 517bool HTMLInputElement::canHaveSelection() const 518{ 519 return isTextField(); 520} 521 522int HTMLInputElement::selectionStartForBinding(ExceptionState& exceptionState) const 523{ 524 if (!m_inputType->supportsSelectionAPI()) { 525 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 526 return 0; 527 } 528 return HTMLTextFormControlElement::selectionStart(); 529} 530 531int HTMLInputElement::selectionEndForBinding(ExceptionState& exceptionState) const 532{ 533 if (!m_inputType->supportsSelectionAPI()) { 534 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 535 return 0; 536 } 537 return HTMLTextFormControlElement::selectionEnd(); 538} 539 540String HTMLInputElement::selectionDirectionForBinding(ExceptionState& exceptionState) const 541{ 542 if (!m_inputType->supportsSelectionAPI()) { 543 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 544 return String(); 545 } 546 return HTMLTextFormControlElement::selectionDirection(); 547} 548 549void HTMLInputElement::setSelectionStartForBinding(int start, ExceptionState& exceptionState) 550{ 551 if (!m_inputType->supportsSelectionAPI()) { 552 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 553 return; 554 } 555 HTMLTextFormControlElement::setSelectionStart(start); 556} 557 558void HTMLInputElement::setSelectionEndForBinding(int end, ExceptionState& exceptionState) 559{ 560 if (!m_inputType->supportsSelectionAPI()) { 561 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 562 return; 563 } 564 HTMLTextFormControlElement::setSelectionEnd(end); 565} 566 567void HTMLInputElement::setSelectionDirectionForBinding(const String& direction, ExceptionState& exceptionState) 568{ 569 if (!m_inputType->supportsSelectionAPI()) { 570 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 571 return; 572 } 573 HTMLTextFormControlElement::setSelectionDirection(direction); 574} 575 576void HTMLInputElement::setSelectionRangeForBinding(int start, int end, ExceptionState& exceptionState) 577{ 578 if (!m_inputType->supportsSelectionAPI()) { 579 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 580 return; 581 } 582 HTMLTextFormControlElement::setSelectionRange(start, end); 583} 584 585void HTMLInputElement::setSelectionRangeForBinding(int start, int end, const String& direction, ExceptionState& exceptionState) 586{ 587 if (!m_inputType->supportsSelectionAPI()) { 588 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 589 return; 590 } 591 HTMLTextFormControlElement::setSelectionRange(start, end, direction); 592} 593 594void HTMLInputElement::accessKeyAction(bool sendMouseEvents) 595{ 596 m_inputType->accessKeyAction(sendMouseEvents); 597} 598 599bool HTMLInputElement::isPresentationAttribute(const QualifiedName& name) const 600{ 601 if (name == vspaceAttr || name == hspaceAttr || name == alignAttr || name == widthAttr || name == heightAttr || (name == borderAttr && isImageButton())) 602 return true; 603 return HTMLTextFormControlElement::isPresentationAttribute(name); 604} 605 606void HTMLInputElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) 607{ 608 if (name == vspaceAttr) { 609 addHTMLLengthToStyle(style, CSSPropertyMarginTop, value); 610 addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value); 611 } else if (name == hspaceAttr) { 612 addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value); 613 addHTMLLengthToStyle(style, CSSPropertyMarginRight, value); 614 } else if (name == alignAttr) { 615 if (m_inputType->shouldRespectAlignAttribute()) 616 applyAlignmentAttributeToStyle(value, style); 617 } else if (name == widthAttr) { 618 if (m_inputType->shouldRespectHeightAndWidthAttributes()) 619 addHTMLLengthToStyle(style, CSSPropertyWidth, value); 620 } else if (name == heightAttr) { 621 if (m_inputType->shouldRespectHeightAndWidthAttributes()) 622 addHTMLLengthToStyle(style, CSSPropertyHeight, value); 623 } else if (name == borderAttr && isImageButton()) 624 applyBorderAttributeToStyle(value, style); 625 else 626 HTMLTextFormControlElement::collectStyleForPresentationAttribute(name, value, style); 627} 628 629void HTMLInputElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 630{ 631 if (name == nameAttr) { 632 removeFromRadioButtonGroup(); 633 m_name = value; 634 addToRadioButtonGroup(); 635 HTMLTextFormControlElement::parseAttribute(name, value); 636 } else if (name == autocompleteAttr) { 637 if (equalIgnoringCase(value, "off")) 638 m_autocomplete = Off; 639 else { 640 if (value.isEmpty()) 641 m_autocomplete = Uninitialized; 642 else 643 m_autocomplete = On; 644 } 645 } else if (name == typeAttr) 646 updateType(); 647 else if (name == valueAttr) { 648 // We only need to setChanged if the form is looking at the default value right now. 649 if (!hasDirtyValue()) { 650 updatePlaceholderVisibility(false); 651 setNeedsStyleRecalc(); 652 } 653 setFormControlValueMatchesRenderer(false); 654 setNeedsValidityCheck(); 655 m_valueAttributeWasUpdatedAfterParsing = !m_parsingInProgress; 656 m_inputTypeView->valueAttributeChanged(); 657 } else if (name == checkedAttr) { 658 // Another radio button in the same group might be checked by state 659 // restore. We shouldn't call setChecked() even if this has the checked 660 // attribute. So, delay the setChecked() call until 661 // finishParsingChildren() is called if parsing is in progress. 662 if (!m_parsingInProgress && m_reflectsCheckedAttribute) { 663 setChecked(!value.isNull()); 664 m_reflectsCheckedAttribute = true; 665 } 666 } else if (name == maxlengthAttr) 667 parseMaxLengthAttribute(value); 668 else if (name == sizeAttr) { 669 int oldSize = m_size; 670 int valueAsInteger = value.toInt(); 671 m_size = valueAsInteger > 0 ? valueAsInteger : defaultSize; 672 if (m_size != oldSize && renderer()) 673 renderer()->setNeedsLayoutAndPrefWidthsRecalc(); 674 } else if (name == altAttr) 675 m_inputTypeView->altAttributeChanged(); 676 else if (name == srcAttr) 677 m_inputTypeView->srcAttributeChanged(); 678 else if (name == usemapAttr || name == accesskeyAttr) { 679 // FIXME: ignore for the moment 680 } else if (name == onsearchAttr) { 681 // Search field and slider attributes all just cause updateFromElement to be called through style recalcing. 682 setAttributeEventListener(EventTypeNames::search, createAttributeEventListener(this, name, value)); 683 } else if (name == resultsAttr) { 684 int oldResults = m_maxResults; 685 m_maxResults = !value.isNull() ? std::min(value.toInt(), maxSavedResults) : -1; 686 // FIXME: Detaching just for maxResults change is not ideal. We should figure out the right 687 // time to relayout for this change. 688 if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0)) 689 lazyReattachIfAttached(); 690 setNeedsStyleRecalc(); 691 UseCounter::count(document(), UseCounter::ResultsAttribute); 692 } else if (name == incrementalAttr) { 693 setNeedsStyleRecalc(); 694 UseCounter::count(document(), UseCounter::IncrementalAttribute); 695 } else if (name == minAttr) { 696 m_inputTypeView->minOrMaxAttributeChanged(); 697 m_inputType->sanitizeValueInResponseToMinOrMaxAttributeChange(); 698 setNeedsValidityCheck(); 699 UseCounter::count(document(), UseCounter::MinAttribute); 700 } else if (name == maxAttr) { 701 m_inputTypeView->minOrMaxAttributeChanged(); 702 setNeedsValidityCheck(); 703 UseCounter::count(document(), UseCounter::MaxAttribute); 704 } else if (name == multipleAttr) { 705 m_inputTypeView->multipleAttributeChanged(); 706 setNeedsValidityCheck(); 707 } else if (name == stepAttr) { 708 m_inputTypeView->stepAttributeChanged(); 709 setNeedsValidityCheck(); 710 UseCounter::count(document(), UseCounter::StepAttribute); 711 } else if (name == patternAttr) { 712 setNeedsValidityCheck(); 713 UseCounter::count(document(), UseCounter::PatternAttribute); 714 } else if (name == precisionAttr) { 715 setNeedsValidityCheck(); 716 UseCounter::count(document(), UseCounter::PrecisionAttribute); 717 } else if (name == disabledAttr) { 718 HTMLTextFormControlElement::parseAttribute(name, value); 719 m_inputTypeView->disabledAttributeChanged(); 720 } else if (name == readonlyAttr) { 721 HTMLTextFormControlElement::parseAttribute(name, value); 722 m_inputTypeView->readonlyAttributeChanged(); 723 } else if (name == listAttr) { 724 m_hasNonEmptyList = !value.isEmpty(); 725 if (m_hasNonEmptyList) { 726 resetListAttributeTargetObserver(); 727 listAttributeTargetChanged(); 728 } 729 UseCounter::count(document(), UseCounter::ListAttribute); 730 } 731#if ENABLE(INPUT_SPEECH) 732 else if (name == webkitspeechAttr) { 733 if (RuntimeEnabledFeatures::speechInputEnabled() && m_inputType->shouldRespectSpeechAttribute()) { 734 // This renderer and its children have quite different layouts and 735 // styles depending on whether the speech button is visible or 736 // not. So we reset the whole thing and recreate to get the right 737 // styles and layout. 738 m_inputTypeView->destroyShadowSubtree(); 739 lazyReattachIfAttached(); 740 m_inputTypeView->createShadowSubtree(); 741 setFormControlValueMatchesRenderer(false); 742 } 743 UseCounter::count(document(), UseCounter::PrefixedSpeechAttribute); 744 } else if (name == onwebkitspeechchangeAttr) 745 setAttributeEventListener(EventTypeNames::webkitspeechchange, createAttributeEventListener(this, name, value)); 746#endif 747 else if (name == webkitdirectoryAttr) { 748 HTMLTextFormControlElement::parseAttribute(name, value); 749 UseCounter::count(document(), UseCounter::PrefixedDirectoryAttribute); 750 } 751 else 752 HTMLTextFormControlElement::parseAttribute(name, value); 753 m_inputTypeView->attributeChanged(); 754} 755 756void HTMLInputElement::finishParsingChildren() 757{ 758 m_parsingInProgress = false; 759 HTMLTextFormControlElement::finishParsingChildren(); 760 if (!m_stateRestored) { 761 bool checked = hasAttribute(checkedAttr); 762 if (checked) 763 setChecked(checked); 764 m_reflectsCheckedAttribute = true; 765 } 766} 767 768bool HTMLInputElement::rendererIsNeeded(const RenderStyle& style) 769{ 770 return m_inputType->rendererIsNeeded() && HTMLTextFormControlElement::rendererIsNeeded(style); 771} 772 773RenderObject* HTMLInputElement::createRenderer(RenderStyle* style) 774{ 775 return m_inputTypeView->createRenderer(style); 776} 777 778void HTMLInputElement::attach(const AttachContext& context) 779{ 780 if (!m_hasType) 781 updateType(); 782 783 HTMLTextFormControlElement::attach(context); 784 785 m_inputTypeView->startResourceLoading(); 786 m_inputType->countUsage(); 787 788 if (document().focusedElement() == this) 789 document().updateFocusAppearanceSoon(true /* restore selection */); 790} 791 792void HTMLInputElement::detach(const AttachContext& context) 793{ 794 HTMLTextFormControlElement::detach(context); 795 setFormControlValueMatchesRenderer(false); 796 m_inputTypeView->closePopupView(); 797} 798 799String HTMLInputElement::altText() const 800{ 801 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen 802 // also heavily discussed by Hixie on bugzilla 803 // note this is intentionally different to HTMLImageElement::altText() 804 String alt = fastGetAttribute(altAttr); 805 // fall back to title attribute 806 if (alt.isNull()) 807 alt = getAttribute(titleAttr); 808 if (alt.isNull()) 809 alt = getAttribute(valueAttr); 810 if (alt.isEmpty()) 811 alt = locale().queryString(blink::WebLocalizedString::InputElementAltText); 812 return alt; 813} 814 815bool HTMLInputElement::canBeSuccessfulSubmitButton() const 816{ 817 return m_inputType->canBeSuccessfulSubmitButton(); 818} 819 820bool HTMLInputElement::isActivatedSubmit() const 821{ 822 return m_isActivatedSubmit; 823} 824 825void HTMLInputElement::setActivatedSubmit(bool flag) 826{ 827 m_isActivatedSubmit = flag; 828} 829 830bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart) 831{ 832 return m_inputType->isFormDataAppendable() && m_inputType->appendFormData(encoding, multipart); 833} 834 835String HTMLInputElement::resultForDialogSubmit() 836{ 837 return m_inputType->resultForDialogSubmit(); 838} 839 840void HTMLInputElement::resetImpl() 841{ 842 if (m_inputType->storesValueSeparateFromAttribute()) 843 setValue(String()); 844 845 setChecked(hasAttribute(checkedAttr)); 846 m_reflectsCheckedAttribute = true; 847} 848 849bool HTMLInputElement::isTextField() const 850{ 851 return m_inputType->isTextField(); 852} 853 854bool HTMLInputElement::isTextType() const 855{ 856 return m_inputType->isTextType(); 857} 858 859void HTMLInputElement::setChecked(bool nowChecked, TextFieldEventBehavior eventBehavior) 860{ 861 if (checked() == nowChecked) 862 return; 863 864 m_reflectsCheckedAttribute = false; 865 m_isChecked = nowChecked; 866 setNeedsStyleRecalc(); 867 868 if (CheckedRadioButtons* buttons = checkedRadioButtons()) 869 buttons->updateCheckedState(this); 870 if (renderer() && renderer()->style()->hasAppearance()) 871 RenderTheme::theme().stateChanged(renderer(), CheckedState); 872 873 setNeedsValidityCheck(); 874 875 // Ideally we'd do this from the render tree (matching 876 // RenderTextView), but it's not possible to do it at the moment 877 // because of the way the code is structured. 878 if (renderer()) { 879 if (AXObjectCache* cache = renderer()->document().existingAXObjectCache()) 880 cache->checkedStateChanged(this); 881 } 882 883 // Only send a change event for items in the document (avoid firing during 884 // parsing) and don't send a change event for a radio button that's getting 885 // unchecked to match other browsers. DOM is not a useful standard for this 886 // because it says only to fire change events at "lose focus" time, which is 887 // definitely wrong in practice for these types of elements. 888 if (eventBehavior != DispatchNoEvent && inDocument() && m_inputType->shouldSendChangeEventAfterCheckedChanged()) { 889 setTextAsOfLastFormControlChangeEvent(String()); 890 dispatchFormControlChangeEvent(); 891 } 892 893 didAffectSelector(AffectedSelectorChecked); 894} 895 896void HTMLInputElement::setIndeterminate(bool newValue) 897{ 898 if (indeterminate() == newValue) 899 return; 900 901 m_isIndeterminate = newValue; 902 903 didAffectSelector(AffectedSelectorIndeterminate); 904 905 if (renderer() && renderer()->style()->hasAppearance()) 906 RenderTheme::theme().stateChanged(renderer(), CheckedState); 907} 908 909int HTMLInputElement::size() const 910{ 911 return m_size; 912} 913 914bool HTMLInputElement::sizeShouldIncludeDecoration(int& preferredSize) const 915{ 916 return m_inputTypeView->sizeShouldIncludeDecoration(defaultSize, preferredSize); 917} 918 919void HTMLInputElement::copyNonAttributePropertiesFromElement(const Element& source) 920{ 921 const HTMLInputElement& sourceElement = static_cast<const HTMLInputElement&>(source); 922 923 m_valueIfDirty = sourceElement.m_valueIfDirty; 924 setChecked(sourceElement.m_isChecked); 925 m_reflectsCheckedAttribute = sourceElement.m_reflectsCheckedAttribute; 926 m_isIndeterminate = sourceElement.m_isIndeterminate; 927 928 HTMLTextFormControlElement::copyNonAttributePropertiesFromElement(source); 929 930 setFormControlValueMatchesRenderer(false); 931 m_inputTypeView->updateView(); 932} 933 934String HTMLInputElement::value() const 935{ 936 String value; 937 if (m_inputType->getTypeSpecificValue(value)) 938 return value; 939 940 value = m_valueIfDirty; 941 if (!value.isNull()) 942 return value; 943 944 AtomicString valueString = fastGetAttribute(valueAttr); 945 value = sanitizeValue(valueString); 946 if (!value.isNull()) 947 return value; 948 949 return m_inputType->fallbackValue(); 950} 951 952String HTMLInputElement::valueWithDefault() const 953{ 954 String value = this->value(); 955 if (!value.isNull()) 956 return value; 957 958 return m_inputType->defaultValue(); 959} 960 961void HTMLInputElement::setValueForUser(const String& value) 962{ 963 // Call setValue and make it send a change event. 964 setValue(value, DispatchChangeEvent); 965} 966 967const String& HTMLInputElement::suggestedValue() const 968{ 969 return m_suggestedValue; 970} 971 972void HTMLInputElement::setSuggestedValue(const String& value) 973{ 974 if (!m_inputType->canSetSuggestedValue()) 975 return; 976 setFormControlValueMatchesRenderer(false); 977 m_suggestedValue = sanitizeValue(value); 978 setNeedsStyleRecalc(); 979 m_inputTypeView->updateView(); 980} 981 982void HTMLInputElement::setEditingValue(const String& value) 983{ 984 if (!renderer() || !isTextField()) 985 return; 986 setInnerTextValue(value); 987 subtreeHasChanged(); 988 989 unsigned max = value.length(); 990 if (focused()) 991 setSelectionRange(max, max); 992 else 993 cacheSelectionInResponseToSetValue(max); 994 995 dispatchInputEvent(); 996} 997 998void HTMLInputElement::setValue(const String& value, ExceptionState& exceptionState, TextFieldEventBehavior eventBehavior) 999{ 1000 if (isFileUpload() && !value.isEmpty()) { 1001 exceptionState.throwDOMException(InvalidStateError, "This input element accepts a filename, which may only be programatically set to the empty string."); 1002 return; 1003 } 1004 setValue(value, eventBehavior); 1005} 1006 1007void HTMLInputElement::setValue(const String& value, TextFieldEventBehavior eventBehavior) 1008{ 1009 if (!m_inputType->canSetValue(value)) 1010 return; 1011 1012 RefPtr<HTMLInputElement> protector(this); 1013 EventQueueScope scope; 1014 String sanitizedValue = sanitizeValue(value); 1015 bool valueChanged = sanitizedValue != this->value(); 1016 1017 setLastChangeWasNotUserEdit(); 1018 setFormControlValueMatchesRenderer(false); 1019 m_suggestedValue = String(); // Prevent TextFieldInputType::setValue from using the suggested value. 1020 1021 m_inputType->setValue(sanitizedValue, valueChanged, eventBehavior); 1022 1023 if (valueChanged && eventBehavior == DispatchNoEvent) 1024 setTextAsOfLastFormControlChangeEvent(sanitizedValue); 1025 1026 if (!valueChanged) 1027 return; 1028 1029 notifyFormStateChanged(); 1030} 1031 1032void HTMLInputElement::setValueInternal(const String& sanitizedValue, TextFieldEventBehavior eventBehavior) 1033{ 1034 m_valueIfDirty = sanitizedValue; 1035 setNeedsValidityCheck(); 1036} 1037 1038double HTMLInputElement::valueAsDate() const 1039{ 1040 return m_inputType->valueAsDate(); 1041} 1042 1043void HTMLInputElement::setValueAsDate(double value, ExceptionState& exceptionState) 1044{ 1045 m_inputType->setValueAsDate(value, exceptionState); 1046} 1047 1048double HTMLInputElement::valueAsNumber() const 1049{ 1050 return m_inputType->valueAsDouble(); 1051} 1052 1053void HTMLInputElement::setValueAsNumber(double newValue, ExceptionState& exceptionState, TextFieldEventBehavior eventBehavior) 1054{ 1055 if (!std::isfinite(newValue)) { 1056 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(newValue)); 1057 return; 1058 } 1059 m_inputType->setValueAsDouble(newValue, eventBehavior, exceptionState); 1060} 1061 1062void HTMLInputElement::setValueFromRenderer(const String& value) 1063{ 1064 // File upload controls will never use this. 1065 ASSERT(!isFileUpload()); 1066 1067 m_suggestedValue = String(); 1068 1069 // Renderer and our event handler are responsible for sanitizing values. 1070 ASSERT(value == sanitizeValue(value) || sanitizeValue(value).isEmpty()); 1071 1072 m_valueIfDirty = value; 1073 1074 setFormControlValueMatchesRenderer(true); 1075 1076 // Input event is fired by the Node::defaultEventHandler for editable controls. 1077 if (!isTextField()) 1078 dispatchInputEvent(); 1079 notifyFormStateChanged(); 1080 1081 setNeedsValidityCheck(); 1082 1083 // Clear autofill flag (and yellow background) on user edit. 1084 setAutofilled(false); 1085} 1086 1087void* HTMLInputElement::preDispatchEventHandler(Event* event) 1088{ 1089 if (event->type() == EventTypeNames::textInput && m_inputTypeView->shouldSubmitImplicitly(event)) { 1090 event->stopPropagation(); 1091 return 0; 1092 } 1093 if (event->type() != EventTypeNames::click) 1094 return 0; 1095 if (!event->isMouseEvent() || toMouseEvent(event)->button() != LeftButton) 1096 return 0; 1097 // FIXME: Check whether there are any cases where this actually ends up leaking. 1098 return m_inputTypeView->willDispatchClick().leakPtr(); 1099} 1100 1101void HTMLInputElement::postDispatchEventHandler(Event* event, void* dataFromPreDispatch) 1102{ 1103 OwnPtr<ClickHandlingState> state = adoptPtr(static_cast<ClickHandlingState*>(dataFromPreDispatch)); 1104 if (!state) 1105 return; 1106 m_inputTypeView->didDispatchClick(event, *state); 1107} 1108 1109void HTMLInputElement::defaultEventHandler(Event* evt) 1110{ 1111 if (evt->isMouseEvent() && evt->type() == EventTypeNames::click && toMouseEvent(evt)->button() == LeftButton) { 1112 m_inputTypeView->handleClickEvent(toMouseEvent(evt)); 1113 if (evt->defaultHandled()) 1114 return; 1115 } 1116 1117 if (evt->isTouchEvent()) { 1118 m_inputTypeView->handleTouchEvent(toTouchEvent(evt)); 1119 if (evt->defaultHandled()) 1120 return; 1121 } 1122 1123 if (evt->isKeyboardEvent() && evt->type() == EventTypeNames::keydown) { 1124 m_inputTypeView->handleKeydownEvent(toKeyboardEvent(evt)); 1125 if (evt->defaultHandled()) 1126 return; 1127 } 1128 1129 // Call the base event handler before any of our own event handling for almost all events in text fields. 1130 // Makes editing keyboard handling take precedence over the keydown and keypress handling in this function. 1131 bool callBaseClassEarly = isTextField() && (evt->type() == EventTypeNames::keydown || evt->type() == EventTypeNames::keypress); 1132 if (callBaseClassEarly) { 1133 HTMLTextFormControlElement::defaultEventHandler(evt); 1134 if (evt->defaultHandled()) 1135 return; 1136 } 1137 1138 // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means 1139 // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks 1140 // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element 1141 // must dispatch a DOMActivate event - a click event will not do the job. 1142 if (evt->type() == EventTypeNames::DOMActivate) { 1143 m_inputType->handleDOMActivateEvent(evt); 1144 if (evt->defaultHandled()) 1145 return; 1146 } 1147 1148 // Use key press event here since sending simulated mouse events 1149 // on key down blocks the proper sending of the key press event. 1150 if (evt->isKeyboardEvent() && evt->type() == EventTypeNames::keypress) { 1151 m_inputTypeView->handleKeypressEvent(toKeyboardEvent(evt)); 1152 if (evt->defaultHandled()) 1153 return; 1154 } 1155 1156 if (evt->isKeyboardEvent() && evt->type() == EventTypeNames::keyup) { 1157 m_inputTypeView->handleKeyupEvent(toKeyboardEvent(evt)); 1158 if (evt->defaultHandled()) 1159 return; 1160 } 1161 1162 if (m_inputTypeView->shouldSubmitImplicitly(evt)) { 1163 if (isSearchField()) 1164 onSearch(); 1165 // Form submission finishes editing, just as loss of focus does. 1166 // If there was a change, send the event now. 1167 if (wasChangedSinceLastFormControlChangeEvent()) 1168 dispatchFormControlChangeEvent(); 1169 1170 RefPtr<HTMLFormElement> formForSubmission = m_inputTypeView->formForSubmission(); 1171 // Form may never have been present, or may have been destroyed by code responding to the change event. 1172 if (formForSubmission) 1173 formForSubmission->submitImplicitly(evt, canTriggerImplicitSubmission()); 1174 1175 evt->setDefaultHandled(); 1176 return; 1177 } 1178 1179 if (evt->isBeforeTextInsertedEvent()) 1180 m_inputTypeView->handleBeforeTextInsertedEvent(static_cast<BeforeTextInsertedEvent*>(evt)); 1181 1182 if (evt->isMouseEvent() && evt->type() == EventTypeNames::mousedown) { 1183 m_inputTypeView->handleMouseDownEvent(toMouseEvent(evt)); 1184 if (evt->defaultHandled()) 1185 return; 1186 } 1187 1188 m_inputTypeView->forwardEvent(evt); 1189 1190 if (!callBaseClassEarly && !evt->defaultHandled()) 1191 HTMLTextFormControlElement::defaultEventHandler(evt); 1192} 1193 1194bool HTMLInputElement::willRespondToMouseClickEvents() 1195{ 1196 // FIXME: Consider implementing willRespondToMouseClickEvents() in InputType if more accurate results are necessary. 1197 if (!isDisabledFormControl()) 1198 return true; 1199 1200 return HTMLTextFormControlElement::willRespondToMouseClickEvents(); 1201} 1202 1203bool HTMLInputElement::isURLAttribute(const Attribute& attribute) const 1204{ 1205 return attribute.name() == srcAttr || attribute.name() == formactionAttr || HTMLTextFormControlElement::isURLAttribute(attribute); 1206} 1207 1208const AtomicString& HTMLInputElement::defaultValue() const 1209{ 1210 return fastGetAttribute(valueAttr); 1211} 1212 1213void HTMLInputElement::setDefaultValue(const AtomicString& value) 1214{ 1215 setAttribute(valueAttr, value); 1216} 1217 1218static inline bool isRFC2616TokenCharacter(UChar ch) 1219{ 1220 return isASCII(ch) && ch > ' ' && ch != '"' && ch != '(' && ch != ')' && ch != ',' && ch != '/' && (ch < ':' || ch > '@') && (ch < '[' || ch > ']') && ch != '{' && ch != '}' && ch != 0x7f; 1221} 1222 1223static bool isValidMIMEType(const String& type) 1224{ 1225 size_t slashPosition = type.find('/'); 1226 if (slashPosition == kNotFound || !slashPosition || slashPosition == type.length() - 1) 1227 return false; 1228 for (size_t i = 0; i < type.length(); ++i) { 1229 if (!isRFC2616TokenCharacter(type[i]) && i != slashPosition) 1230 return false; 1231 } 1232 return true; 1233} 1234 1235static bool isValidFileExtension(const String& type) 1236{ 1237 if (type.length() < 2) 1238 return false; 1239 return type[0] == '.'; 1240} 1241 1242static Vector<String> parseAcceptAttribute(const String& acceptString, bool (*predicate)(const String&)) 1243{ 1244 Vector<String> types; 1245 if (acceptString.isEmpty()) 1246 return types; 1247 1248 Vector<String> splitTypes; 1249 acceptString.split(',', false, splitTypes); 1250 for (size_t i = 0; i < splitTypes.size(); ++i) { 1251 String trimmedType = stripLeadingAndTrailingHTMLSpaces(splitTypes[i]); 1252 if (trimmedType.isEmpty()) 1253 continue; 1254 if (!predicate(trimmedType)) 1255 continue; 1256 types.append(trimmedType.lower()); 1257 } 1258 1259 return types; 1260} 1261 1262Vector<String> HTMLInputElement::acceptMIMETypes() 1263{ 1264 return parseAcceptAttribute(fastGetAttribute(acceptAttr), isValidMIMEType); 1265} 1266 1267Vector<String> HTMLInputElement::acceptFileExtensions() 1268{ 1269 return parseAcceptAttribute(fastGetAttribute(acceptAttr), isValidFileExtension); 1270} 1271 1272const AtomicString& HTMLInputElement::accept() const 1273{ 1274 return fastGetAttribute(acceptAttr); 1275} 1276 1277const AtomicString& HTMLInputElement::alt() const 1278{ 1279 return fastGetAttribute(altAttr); 1280} 1281 1282int HTMLInputElement::maxLength() const 1283{ 1284 return m_maxLength; 1285} 1286 1287void HTMLInputElement::setMaxLength(int maxLength, ExceptionState& exceptionState) 1288{ 1289 if (maxLength < 0) 1290 exceptionState.throwDOMException(IndexSizeError, "The value provided (" + String::number(maxLength) + ") is negative."); 1291 else 1292 setIntegralAttribute(maxlengthAttr, maxLength); 1293} 1294 1295bool HTMLInputElement::multiple() const 1296{ 1297 return fastHasAttribute(multipleAttr); 1298} 1299 1300void HTMLInputElement::setSize(unsigned size) 1301{ 1302 setUnsignedIntegralAttribute(sizeAttr, size); 1303} 1304 1305void HTMLInputElement::setSize(unsigned size, ExceptionState& exceptionState) 1306{ 1307 if (!size) 1308 exceptionState.throwDOMException(IndexSizeError, "The value provided is 0, which is an invalid size."); 1309 else 1310 setSize(size); 1311} 1312 1313KURL HTMLInputElement::src() const 1314{ 1315 return document().completeURL(fastGetAttribute(srcAttr)); 1316} 1317 1318FileList* HTMLInputElement::files() 1319{ 1320 return m_inputType->files(); 1321} 1322 1323void HTMLInputElement::setFiles(PassRefPtr<FileList> files) 1324{ 1325 m_inputType->setFiles(files); 1326} 1327 1328bool HTMLInputElement::receiveDroppedFiles(const DragData* dragData) 1329{ 1330 return m_inputType->receiveDroppedFiles(dragData); 1331} 1332 1333String HTMLInputElement::droppedFileSystemId() 1334{ 1335 return m_inputType->droppedFileSystemId(); 1336} 1337 1338bool HTMLInputElement::canReceiveDroppedFiles() const 1339{ 1340 return m_canReceiveDroppedFiles; 1341} 1342 1343void HTMLInputElement::setCanReceiveDroppedFiles(bool canReceiveDroppedFiles) 1344{ 1345 if (m_canReceiveDroppedFiles == canReceiveDroppedFiles) 1346 return; 1347 m_canReceiveDroppedFiles = canReceiveDroppedFiles; 1348 if (renderer()) 1349 renderer()->updateFromElement(); 1350} 1351 1352String HTMLInputElement::visibleValue() const 1353{ 1354 return m_inputType->visibleValue(); 1355} 1356 1357String HTMLInputElement::sanitizeValue(const String& proposedValue) const 1358{ 1359 if (proposedValue.isNull()) 1360 return proposedValue; 1361 return m_inputType->sanitizeValue(proposedValue); 1362} 1363 1364String HTMLInputElement::localizeValue(const String& proposedValue) const 1365{ 1366 if (proposedValue.isNull()) 1367 return proposedValue; 1368 return m_inputType->localizeValue(proposedValue); 1369} 1370 1371bool HTMLInputElement::isInRange() const 1372{ 1373 return m_inputType->isInRange(value()); 1374} 1375 1376bool HTMLInputElement::isOutOfRange() const 1377{ 1378 return m_inputType->isOutOfRange(value()); 1379} 1380 1381bool HTMLInputElement::isRequiredFormControl() const 1382{ 1383 return m_inputType->supportsRequired() && isRequired(); 1384} 1385 1386bool HTMLInputElement::matchesReadOnlyPseudoClass() const 1387{ 1388 return m_inputType->supportsReadOnly() && isReadOnly(); 1389} 1390 1391bool HTMLInputElement::matchesReadWritePseudoClass() const 1392{ 1393 return m_inputType->supportsReadOnly() && !isReadOnly(); 1394} 1395 1396void HTMLInputElement::onSearch() 1397{ 1398 ASSERT(isSearchField()); 1399 if (m_inputType) 1400 static_cast<SearchInputType*>(m_inputType.get())->stopSearchEventTimer(); 1401 dispatchEvent(Event::createBubble(EventTypeNames::search)); 1402} 1403 1404void HTMLInputElement::updateClearButtonVisibility() 1405{ 1406 m_inputTypeView->updateClearButtonVisibility(); 1407} 1408 1409void HTMLInputElement::willChangeForm() 1410{ 1411 removeFromRadioButtonGroup(); 1412 HTMLTextFormControlElement::willChangeForm(); 1413} 1414 1415void HTMLInputElement::didChangeForm() 1416{ 1417 HTMLTextFormControlElement::didChangeForm(); 1418 addToRadioButtonGroup(); 1419} 1420 1421Node::InsertionNotificationRequest HTMLInputElement::insertedInto(ContainerNode* insertionPoint) 1422{ 1423 HTMLTextFormControlElement::insertedInto(insertionPoint); 1424 if (insertionPoint->inDocument() && !form()) 1425 addToRadioButtonGroup(); 1426 resetListAttributeTargetObserver(); 1427 return InsertionDone; 1428} 1429 1430void HTMLInputElement::removedFrom(ContainerNode* insertionPoint) 1431{ 1432 if (insertionPoint->inDocument() && !form()) 1433 removeFromRadioButtonGroup(); 1434 HTMLTextFormControlElement::removedFrom(insertionPoint); 1435 ASSERT(!inDocument()); 1436 resetListAttributeTargetObserver(); 1437} 1438 1439void HTMLInputElement::didMoveToNewDocument(Document& oldDocument) 1440{ 1441 if (hasImageLoader()) 1442 imageLoader()->elementDidMoveToNewDocument(); 1443 1444 if (isRadioButton()) 1445 oldDocument.formController()->checkedRadioButtons().removeButton(this); 1446 if (m_hasTouchEventHandler) 1447 oldDocument.didRemoveEventTargetNode(this); 1448 1449 if (m_hasTouchEventHandler) 1450 document().didAddTouchEventHandler(this); 1451 1452 HTMLTextFormControlElement::didMoveToNewDocument(oldDocument); 1453} 1454 1455void HTMLInputElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const 1456{ 1457 HTMLTextFormControlElement::addSubresourceAttributeURLs(urls); 1458 1459 addSubresourceURL(urls, src()); 1460} 1461 1462bool HTMLInputElement::recalcWillValidate() const 1463{ 1464 return m_inputType->supportsValidation() && HTMLTextFormControlElement::recalcWillValidate(); 1465} 1466 1467void HTMLInputElement::requiredAttributeChanged() 1468{ 1469 HTMLTextFormControlElement::requiredAttributeChanged(); 1470 if (CheckedRadioButtons* buttons = checkedRadioButtons()) 1471 buttons->requiredAttributeChanged(this); 1472 m_inputTypeView->requiredAttributeChanged(); 1473} 1474 1475void HTMLInputElement::selectColorInColorChooser(const Color& color) 1476{ 1477 if (!m_inputType->isColorControl()) 1478 return; 1479 static_cast<ColorInputType*>(m_inputType.get())->didChooseColor(color); 1480} 1481 1482HTMLElement* HTMLInputElement::list() const 1483{ 1484 return dataList(); 1485} 1486 1487HTMLDataListElement* HTMLInputElement::dataList() const 1488{ 1489 if (!m_hasNonEmptyList) 1490 return 0; 1491 1492 if (!m_inputType->shouldRespectListAttribute()) 1493 return 0; 1494 1495 Element* element = treeScope().getElementById(fastGetAttribute(listAttr)); 1496 if (!element) 1497 return 0; 1498 if (!element->hasTagName(datalistTag)) 1499 return 0; 1500 1501 return toHTMLDataListElement(element); 1502} 1503 1504bool HTMLInputElement::hasValidDataListOptions() const 1505{ 1506 HTMLDataListElement* dataList = this->dataList(); 1507 if (!dataList) 1508 return false; 1509 RefPtr<HTMLCollection> options = dataList->options(); 1510 for (unsigned i = 0; HTMLOptionElement* option = toHTMLOptionElement(options->item(i)); ++i) { 1511 if (isValidValue(option->value())) 1512 return true; 1513 } 1514 return false; 1515} 1516 1517void HTMLInputElement::resetListAttributeTargetObserver() 1518{ 1519 if (inDocument()) 1520 m_listAttributeTargetObserver = ListAttributeTargetObserver::create(fastGetAttribute(listAttr), this); 1521 else 1522 m_listAttributeTargetObserver = nullptr; 1523} 1524 1525void HTMLInputElement::listAttributeTargetChanged() 1526{ 1527 m_inputTypeView->listAttributeTargetChanged(); 1528} 1529 1530bool HTMLInputElement::isSteppable() const 1531{ 1532 return m_inputType->isSteppable(); 1533} 1534 1535#if ENABLE(INPUT_SPEECH) 1536 1537bool HTMLInputElement::isSpeechEnabled() const 1538{ 1539 // FIXME: Add support for RANGE, EMAIL, URL, COLOR and DATE/TIME input types. 1540 return m_inputType->shouldRespectSpeechAttribute() && RuntimeEnabledFeatures::speechInputEnabled() && hasAttribute(webkitspeechAttr); 1541} 1542 1543#endif 1544 1545bool HTMLInputElement::isTextButton() const 1546{ 1547 return m_inputType->isTextButton(); 1548} 1549 1550bool HTMLInputElement::isRadioButton() const 1551{ 1552 return m_inputType->isRadioButton(); 1553} 1554 1555bool HTMLInputElement::isSearchField() const 1556{ 1557 return m_inputType->isSearchField(); 1558} 1559 1560bool HTMLInputElement::isInputTypeHidden() const 1561{ 1562 return m_inputType->isHiddenType(); 1563} 1564 1565bool HTMLInputElement::isPasswordField() const 1566{ 1567 return m_inputType->isPasswordField(); 1568} 1569 1570bool HTMLInputElement::isCheckbox() const 1571{ 1572 return m_inputType->isCheckbox(); 1573} 1574 1575bool HTMLInputElement::isRangeControl() const 1576{ 1577 return m_inputType->isRangeControl(); 1578} 1579 1580bool HTMLInputElement::isColorControl() const 1581{ 1582 return m_inputType->isColorControl(); 1583} 1584 1585bool HTMLInputElement::isText() const 1586{ 1587 return m_inputType->isTextType(); 1588} 1589 1590bool HTMLInputElement::isEmailField() const 1591{ 1592 return m_inputType->isEmailField(); 1593} 1594 1595bool HTMLInputElement::isFileUpload() const 1596{ 1597 return m_inputType->isFileUpload(); 1598} 1599 1600bool HTMLInputElement::isImageButton() const 1601{ 1602 return m_inputType->isImageButton(); 1603} 1604 1605bool HTMLInputElement::isNumberField() const 1606{ 1607 return m_inputType->isNumberField(); 1608} 1609 1610bool HTMLInputElement::isSubmitButton() const 1611{ 1612 return m_inputType->isSubmitButton(); 1613} 1614 1615bool HTMLInputElement::isTelephoneField() const 1616{ 1617 return m_inputType->isTelephoneField(); 1618} 1619 1620bool HTMLInputElement::isURLField() const 1621{ 1622 return m_inputType->isURLField(); 1623} 1624 1625bool HTMLInputElement::isDateField() const 1626{ 1627 return m_inputType->isDateField(); 1628} 1629 1630bool HTMLInputElement::isDateTimeLocalField() const 1631{ 1632 return m_inputType->isDateTimeLocalField(); 1633} 1634 1635bool HTMLInputElement::isMonthField() const 1636{ 1637 return m_inputType->isMonthField(); 1638} 1639 1640bool HTMLInputElement::isTimeField() const 1641{ 1642 return m_inputType->isTimeField(); 1643} 1644 1645bool HTMLInputElement::isWeekField() const 1646{ 1647 return m_inputType->isWeekField(); 1648} 1649 1650bool HTMLInputElement::isEnumeratable() const 1651{ 1652 return m_inputType->isEnumeratable(); 1653} 1654 1655bool HTMLInputElement::supportLabels() const 1656{ 1657 return m_inputType->isInteractiveContent(); 1658} 1659 1660bool HTMLInputElement::shouldAppearChecked() const 1661{ 1662 return checked() && m_inputType->isCheckable(); 1663} 1664 1665bool HTMLInputElement::supportsPlaceholder() const 1666{ 1667 return m_inputType->supportsPlaceholder(); 1668} 1669 1670void HTMLInputElement::updatePlaceholderText() 1671{ 1672 return m_inputType->updatePlaceholderText(); 1673} 1674 1675void HTMLInputElement::parseMaxLengthAttribute(const AtomicString& value) 1676{ 1677 int maxLength; 1678 if (!parseHTMLInteger(value, maxLength)) 1679 maxLength = maximumLength; 1680 if (maxLength < 0 || maxLength > maximumLength) 1681 maxLength = maximumLength; 1682 int oldMaxLength = m_maxLength; 1683 m_maxLength = maxLength; 1684 if (oldMaxLength != maxLength) 1685 updateValueIfNeeded(); 1686 setNeedsStyleRecalc(); 1687 setNeedsValidityCheck(); 1688} 1689 1690void HTMLInputElement::updateValueIfNeeded() 1691{ 1692 String newValue = sanitizeValue(m_valueIfDirty); 1693 ASSERT(!m_valueIfDirty.isNull() || newValue.isNull()); 1694 if (newValue != m_valueIfDirty) 1695 setValue(newValue); 1696} 1697 1698String HTMLInputElement::defaultToolTip() const 1699{ 1700 return m_inputType->defaultToolTip(); 1701} 1702 1703bool HTMLInputElement::shouldAppearIndeterminate() const 1704{ 1705 return m_inputType->supportsIndeterminateAppearance() && indeterminate(); 1706} 1707 1708#if ENABLE(MEDIA_CAPTURE) 1709bool HTMLInputElement::capture() const 1710{ 1711 if (!isFileUpload() || !fastHasAttribute(captureAttr)) 1712 return false; 1713 1714 // As per crbug.com/240252, emit a deprecation warning when the "capture" 1715 // attribute is used as an enum. The spec has been updated and "capture" is 1716 // supposed to be used as a boolean. 1717 bool hasDeprecatedUsage = !fastGetAttribute(captureAttr).isNull(); 1718 if (hasDeprecatedUsage) 1719 UseCounter::countDeprecation(document(), UseCounter::CaptureAttributeAsEnum); 1720 else 1721 UseCounter::count(document(), UseCounter::CaptureAttributeAsEnum); 1722 1723 return true; 1724} 1725#endif 1726 1727bool HTMLInputElement::isInRequiredRadioButtonGroup() 1728{ 1729 ASSERT(isRadioButton()); 1730 if (CheckedRadioButtons* buttons = checkedRadioButtons()) 1731 return buttons->isInRequiredGroup(this); 1732 return false; 1733} 1734 1735HTMLInputElement* HTMLInputElement::checkedRadioButtonForGroup() const 1736{ 1737 if (CheckedRadioButtons* buttons = checkedRadioButtons()) 1738 return buttons->checkedButtonForGroup(name()); 1739 return 0; 1740} 1741 1742CheckedRadioButtons* HTMLInputElement::checkedRadioButtons() const 1743{ 1744 if (!isRadioButton()) 1745 return 0; 1746 if (HTMLFormElement* formElement = form()) 1747 return &formElement->checkedRadioButtons(); 1748 if (inDocument()) 1749 return &document().formController()->checkedRadioButtons(); 1750 return 0; 1751} 1752 1753inline void HTMLInputElement::addToRadioButtonGroup() 1754{ 1755 if (CheckedRadioButtons* buttons = checkedRadioButtons()) 1756 buttons->addButton(this); 1757} 1758 1759inline void HTMLInputElement::removeFromRadioButtonGroup() 1760{ 1761 if (CheckedRadioButtons* buttons = checkedRadioButtons()) 1762 buttons->removeButton(this); 1763} 1764 1765unsigned HTMLInputElement::height() const 1766{ 1767 return m_inputType->height(); 1768} 1769 1770unsigned HTMLInputElement::width() const 1771{ 1772 return m_inputType->width(); 1773} 1774 1775void HTMLInputElement::setHeight(unsigned height) 1776{ 1777 setUnsignedIntegralAttribute(heightAttr, height); 1778} 1779 1780void HTMLInputElement::setWidth(unsigned width) 1781{ 1782 setUnsignedIntegralAttribute(widthAttr, width); 1783} 1784 1785PassOwnPtr<ListAttributeTargetObserver> ListAttributeTargetObserver::create(const AtomicString& id, HTMLInputElement* element) 1786{ 1787 return adoptPtr(new ListAttributeTargetObserver(id, element)); 1788} 1789 1790ListAttributeTargetObserver::ListAttributeTargetObserver(const AtomicString& id, HTMLInputElement* element) 1791 : IdTargetObserver(element->treeScope().idTargetObserverRegistry(), id) 1792 , m_element(element) 1793{ 1794} 1795 1796void ListAttributeTargetObserver::idTargetChanged() 1797{ 1798 m_element->listAttributeTargetChanged(); 1799} 1800 1801void HTMLInputElement::setRangeText(const String& replacement, ExceptionState& exceptionState) 1802{ 1803 if (!m_inputType->supportsSelectionAPI()) { 1804 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 1805 return; 1806 } 1807 1808 HTMLTextFormControlElement::setRangeText(replacement, exceptionState); 1809} 1810 1811void HTMLInputElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode, ExceptionState& exceptionState) 1812{ 1813 if (!m_inputType->supportsSelectionAPI()) { 1814 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 1815 return; 1816 } 1817 1818 HTMLTextFormControlElement::setRangeText(replacement, start, end, selectionMode, exceptionState); 1819} 1820 1821bool HTMLInputElement::setupDateTimeChooserParameters(DateTimeChooserParameters& parameters) 1822{ 1823 if (!document().view()) 1824 return false; 1825 1826 parameters.type = type(); 1827 parameters.minimum = minimum(); 1828 parameters.maximum = maximum(); 1829 parameters.required = isRequired(); 1830 if (!RuntimeEnabledFeatures::langAttributeAwareFormControlUIEnabled()) 1831 parameters.locale = defaultLanguage(); 1832 else { 1833 AtomicString computedLocale = computeInheritedLanguage(); 1834 parameters.locale = computedLocale.isEmpty() ? AtomicString(defaultLanguage()) : computedLocale; 1835 } 1836 1837 StepRange stepRange = createStepRange(RejectAny); 1838 if (stepRange.hasStep()) { 1839 parameters.step = stepRange.step().toDouble(); 1840 parameters.stepBase = stepRange.stepBase().toDouble(); 1841 } else { 1842 parameters.step = 1.0; 1843 parameters.stepBase = 0; 1844 } 1845 1846 parameters.anchorRectInRootView = document().view()->contentsToRootView(pixelSnappedBoundingBox()); 1847 parameters.currentValue = value(); 1848 parameters.doubleValue = m_inputType->valueAsDouble(); 1849 parameters.isAnchorElementRTL = computedStyle()->direction() == RTL; 1850 if (RuntimeEnabledFeatures::dataListElementEnabled()) { 1851 if (HTMLDataListElement* dataList = this->dataList()) { 1852 RefPtr<HTMLCollection> options = dataList->options(); 1853 for (unsigned i = 0; HTMLOptionElement* option = toHTMLOptionElement(options->item(i)); ++i) { 1854 if (!isValidValue(option->value())) 1855 continue; 1856 DateTimeSuggestion suggestion; 1857 suggestion.value = m_inputType->parseToNumber(option->value(), Decimal::nan()).toDouble(); 1858 if (std::isnan(suggestion.value)) 1859 continue; 1860 suggestion.localizedValue = localizeValue(option->value()); 1861 suggestion.label = option->value() == option->label() ? String() : option->label(); 1862 parameters.suggestions.append(suggestion); 1863 } 1864 } 1865 } 1866 return true; 1867} 1868 1869bool HTMLInputElement::supportsInputModeAttribute() const 1870{ 1871 return m_inputType->supportsInputModeAttribute(); 1872} 1873 1874bool HTMLInputElement::isInteractiveContent() const 1875{ 1876 return m_inputType->isInteractiveContent(); 1877} 1878 1879#if ENABLE(INPUT_MULTIPLE_FIELDS_UI) 1880PassRefPtr<RenderStyle> HTMLInputElement::customStyleForRenderer() 1881{ 1882 return m_inputTypeView->customStyleForRenderer(originalStyleForRenderer()); 1883} 1884#endif 1885 1886} // namespace 1887