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