Keep auxilliary media objects on the heap always.
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / HTMLInputElement.cpp
blobf14561f81f06a0416f789f4e2a8c18d358011798
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.
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.
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.
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.
29 #include "config.h"
30 #include "core/html/HTMLInputElement.h"
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/dom/AXObjectCache.h"
40 #include "core/dom/Document.h"
41 #include "core/dom/IdTargetObserver.h"
42 #include "core/dom/shadow/InsertionPoint.h"
43 #include "core/dom/shadow/ShadowRoot.h"
44 #include "core/editing/FrameSelection.h"
45 #include "core/editing/spellcheck/SpellChecker.h"
46 #include "core/events/BeforeTextInsertedEvent.h"
47 #include "core/events/KeyboardEvent.h"
48 #include "core/events/MouseEvent.h"
49 #include "core/events/ScopedEventQueue.h"
50 #include "core/events/TouchEvent.h"
51 #include "core/frame/EventHandlerRegistry.h"
52 #include "core/frame/FrameHost.h"
53 #include "core/frame/FrameView.h"
54 #include "core/frame/LocalFrame.h"
55 #include "core/frame/UseCounter.h"
56 #include "core/html/HTMLCollection.h"
57 #include "core/html/HTMLDataListElement.h"
58 #include "core/html/HTMLDataListOptionsCollection.h"
59 #include "core/html/HTMLFormElement.h"
60 #include "core/html/HTMLImageLoader.h"
61 #include "core/html/HTMLOptionElement.h"
62 #include "core/html/forms/ColorChooser.h"
63 #include "core/html/forms/DateTimeChooser.h"
64 #include "core/html/forms/FileInputType.h"
65 #include "core/html/forms/FormController.h"
66 #include "core/html/forms/InputType.h"
67 #include "core/html/forms/SearchInputType.h"
68 #include "core/html/parser/HTMLParserIdioms.h"
69 #include "core/layout/LayoutTextControlSingleLine.h"
70 #include "core/layout/LayoutTheme.h"
71 #include "core/page/ChromeClient.h"
72 #include "platform/Language.h"
73 #include "platform/PlatformMouseEvent.h"
74 #include "platform/RuntimeEnabledFeatures.h"
75 #include "platform/text/PlatformLocale.h"
76 #include "wtf/MathExtras.h"
78 namespace blink {
80 using namespace HTMLNames;
82 class ListAttributeTargetObserver : public IdTargetObserver {
83 WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED(ListAttributeTargetObserver);
84 public:
85 static PassOwnPtrWillBeRawPtr<ListAttributeTargetObserver> create(const AtomicString& id, HTMLInputElement*);
86 DECLARE_VIRTUAL_TRACE();
87 void idTargetChanged() override;
89 private:
90 ListAttributeTargetObserver(const AtomicString& id, HTMLInputElement*);
92 RawPtrWillBeMember<HTMLInputElement> m_element;
95 // FIXME: According to HTML4, the length attribute's value can be arbitrarily
96 // large. However, due to https://bugs.webkit.org/show_bug.cgi?id=14536 things
97 // get rather sluggish when a text field has a larger number of characters than
98 // this, even when just clicking in the text field.
99 const int HTMLInputElement::maximumLength = 524288;
100 const int defaultSize = 20;
101 const int maxSavedResults = 256;
103 HTMLInputElement::HTMLInputElement(Document& document, HTMLFormElement* form, bool createdByParser)
104 : HTMLTextFormControlElement(inputTag, document, form)
105 , m_size(defaultSize)
106 , m_maxLength(maximumLength)
107 , m_minLength(0)
108 , m_maxResults(-1)
109 , m_isChecked(false)
110 , m_reflectsCheckedAttribute(true)
111 , m_isIndeterminate(false)
112 , m_isActivatedSubmit(false)
113 , m_autocomplete(Uninitialized)
114 , m_hasNonEmptyList(false)
115 , m_stateRestored(false)
116 , m_parsingInProgress(createdByParser)
117 , m_valueAttributeWasUpdatedAfterParsing(false)
118 , m_canReceiveDroppedFiles(false)
119 , m_hasTouchEventHandler(false)
120 , m_shouldRevealPassword(false)
121 , m_needsToUpdateViewValue(true)
122 // |m_inputType| is lazily created when constructed by the parser to avoid
123 // constructing unnecessarily a text inputType and its shadow subtree, just
124 // to destroy them when the |type| attribute gets set by the parser to
125 // something else than 'text'.
126 , m_inputType(createdByParser ? nullptr : InputType::createText(*this))
127 , m_inputTypeView(m_inputType)
129 setHasCustomStyleCallbacks();
132 PassRefPtrWillBeRawPtr<HTMLInputElement> HTMLInputElement::create(Document& document, HTMLFormElement* form, bool createdByParser)
134 RefPtrWillBeRawPtr<HTMLInputElement> inputElement = adoptRefWillBeNoop(new HTMLInputElement(document, form, createdByParser));
135 if (!createdByParser)
136 inputElement->ensureUserAgentShadowRoot();
137 return inputElement.release();
140 DEFINE_TRACE(HTMLInputElement)
142 visitor->trace(m_inputType);
143 visitor->trace(m_inputTypeView);
144 visitor->trace(m_listAttributeTargetObserver);
145 visitor->trace(m_imageLoader);
146 HTMLTextFormControlElement::trace(visitor);
149 HTMLImageLoader& HTMLInputElement::ensureImageLoader()
151 if (!m_imageLoader)
152 m_imageLoader = HTMLImageLoader::create(this);
153 return *m_imageLoader;
156 void HTMLInputElement::didAddUserAgentShadowRoot(ShadowRoot&)
158 m_inputTypeView->createShadowSubtree();
161 void HTMLInputElement::willAddFirstAuthorShadowRoot()
163 m_inputTypeView->destroyShadowSubtree();
164 m_inputTypeView = InputTypeView::create(*this);
165 lazyReattachIfAttached();
168 HTMLInputElement::~HTMLInputElement()
170 #if !ENABLE(OILPAN)
171 // Need to remove form association while this is still an HTMLInputElement
172 // so that virtual functions are called correctly.
173 setForm(0);
174 // setForm(0) may register this to a document-level radio button group.
175 // We should unregister it to avoid accessing a deleted object.
176 if (type() == InputTypeNames::radio)
177 document().formController().radioButtonGroupScope().removeButton(this);
178 if (m_hasTouchEventHandler && document().frameHost())
179 document().frameHost()->eventHandlerRegistry().didRemoveEventHandler(*this, EventHandlerRegistry::TouchEvent);
180 #endif
183 const AtomicString& HTMLInputElement::name() const
185 return m_name.isNull() ? emptyAtom : m_name;
188 Vector<FileChooserFileInfo> HTMLInputElement::filesFromFileInputFormControlState(const FormControlState& state)
190 return FileInputType::filesFromFormControlState(state);
193 bool HTMLInputElement::shouldAutocomplete() const
195 if (m_autocomplete != Uninitialized)
196 return m_autocomplete == On;
197 return HTMLTextFormControlElement::shouldAutocomplete();
200 bool HTMLInputElement::isValidValue(const String& value) const
202 if (!m_inputType->canSetStringValue()) {
203 ASSERT_NOT_REACHED();
204 return false;
206 return !m_inputType->typeMismatchFor(value)
207 && !m_inputType->stepMismatch(value)
208 && !m_inputType->rangeUnderflow(value)
209 && !m_inputType->rangeOverflow(value)
210 && !tooLong(value, IgnoreDirtyFlag)
211 && !tooShort(value, IgnoreDirtyFlag)
212 && !m_inputType->patternMismatch(value)
213 && !m_inputType->valueMissing(value);
216 bool HTMLInputElement::tooLong() const
218 return willValidate() && tooLong(value(), CheckDirtyFlag);
221 bool HTMLInputElement::tooShort() const
223 return willValidate() && tooShort(value(), CheckDirtyFlag);
226 bool HTMLInputElement::typeMismatch() const
228 return willValidate() && m_inputType->typeMismatch();
231 bool HTMLInputElement::valueMissing() const
233 return willValidate() && m_inputType->valueMissing(value());
236 bool HTMLInputElement::hasBadInput() const
238 return willValidate() && m_inputType->hasBadInput();
241 bool HTMLInputElement::patternMismatch() const
243 return willValidate() && m_inputType->patternMismatch(value());
246 bool HTMLInputElement::tooLong(const String& value, NeedsToCheckDirtyFlag check) const
248 return m_inputType->tooLong(value, check);
251 bool HTMLInputElement::tooShort(const String& value, NeedsToCheckDirtyFlag check) const
253 return m_inputType->tooShort(value, check);
256 bool HTMLInputElement::rangeUnderflow() const
258 return willValidate() && m_inputType->rangeUnderflow(value());
261 bool HTMLInputElement::rangeOverflow() const
263 return willValidate() && m_inputType->rangeOverflow(value());
266 String HTMLInputElement::validationMessage() const
268 if (!willValidate())
269 return String();
271 if (customError())
272 return customValidationMessage();
274 return m_inputType->validationMessage();
277 double HTMLInputElement::minimum() const
279 return m_inputType->minimum();
282 double HTMLInputElement::maximum() const
284 return m_inputType->maximum();
287 bool HTMLInputElement::stepMismatch() const
289 return willValidate() && m_inputType->stepMismatch(value());
292 bool HTMLInputElement::getAllowedValueStep(Decimal* step) const
294 return m_inputType->getAllowedValueStep(step);
297 StepRange HTMLInputElement::createStepRange(AnyStepHandling anyStepHandling) const
299 return m_inputType->createStepRange(anyStepHandling);
302 Decimal HTMLInputElement::findClosestTickMarkValue(const Decimal& value)
304 return m_inputType->findClosestTickMarkValue(value);
307 void HTMLInputElement::stepUp(int n, ExceptionState& exceptionState)
309 m_inputType->stepUp(n, exceptionState);
312 void HTMLInputElement::stepDown(int n, ExceptionState& exceptionState)
314 m_inputType->stepUp(-n, exceptionState);
317 void HTMLInputElement::blur()
319 m_inputTypeView->blur();
322 void HTMLInputElement::defaultBlur()
324 HTMLTextFormControlElement::blur();
327 bool HTMLInputElement::hasCustomFocusLogic() const
329 return m_inputTypeView->hasCustomFocusLogic();
332 bool HTMLInputElement::isKeyboardFocusable() const
334 return m_inputType->isKeyboardFocusable();
337 bool HTMLInputElement::shouldShowFocusRingOnMouseFocus() const
339 return m_inputType->shouldShowFocusRingOnMouseFocus();
342 void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
344 if (isTextField()) {
345 if (!restorePreviousSelection)
346 select(NotDispatchSelectEvent);
347 else
348 restoreCachedSelection();
349 if (document().frame())
350 document().frame()->selection().revealSelection();
351 } else {
352 HTMLTextFormControlElement::updateFocusAppearance(restorePreviousSelection);
356 void HTMLInputElement::beginEditing()
358 ASSERT(document().isActive());
359 if (!document().isActive())
360 return;
362 if (!isTextField())
363 return;
365 document().frame()->spellChecker().didBeginEditing(this);
368 void HTMLInputElement::endEditing()
370 ASSERT(document().isActive());
371 if (!document().isActive())
372 return;
374 if (!isTextField())
375 return;
377 LocalFrame* frame = document().frame();
378 frame->spellChecker().didEndEditingOnTextField(this);
379 frame->host()->chromeClient().didEndEditingOnTextField(*this);
382 void HTMLInputElement::handleFocusEvent(Element* oldFocusedElement, WebFocusType type)
384 m_inputTypeView->handleFocusEvent(oldFocusedElement, type);
385 m_inputType->enableSecureTextInput();
388 void HTMLInputElement::dispatchFocusInEvent(const AtomicString& eventType, Element* oldFocusedElement, WebFocusType type, InputDeviceCapabilities* sourceCapabilities)
390 if (eventType == EventTypeNames::DOMFocusIn)
391 m_inputTypeView->handleFocusInEvent(oldFocusedElement, type);
392 HTMLFormControlElementWithState::dispatchFocusInEvent(eventType, oldFocusedElement, type, sourceCapabilities);
395 void HTMLInputElement::handleBlurEvent()
397 m_inputType->disableSecureTextInput();
398 m_inputTypeView->handleBlurEvent();
401 void HTMLInputElement::setType(const AtomicString& type)
403 setAttribute(typeAttr, type);
406 void HTMLInputElement::updateTouchEventHandlerRegistry()
408 ASSERT(m_inputTypeView);
410 bool hasTouchEventHandler = m_inputTypeView->hasTouchEventHandler();
411 if (hasTouchEventHandler == !!m_hasTouchEventHandler)
412 return;
413 // If the Document is being or has been stopped, don't register any handlers.
414 if (document().frameHost() && document().lifecycle().state() < DocumentLifecycle::Stopping) {
415 EventHandlerRegistry& registry = document().frameHost()->eventHandlerRegistry();
416 if (hasTouchEventHandler)
417 registry.didAddEventHandler(*this, EventHandlerRegistry::TouchEvent);
418 else
419 registry.didRemoveEventHandler(*this, EventHandlerRegistry::TouchEvent);
421 m_hasTouchEventHandler = hasTouchEventHandler;
424 void HTMLInputElement::initializeTypeInParsing()
426 ASSERT(m_parsingInProgress);
427 ASSERT(!m_inputType);
428 ASSERT(!m_inputTypeView);
430 const AtomicString& newTypeName = InputType::normalizeTypeName(fastGetAttribute(typeAttr));
431 m_inputType = InputType::create(*this, newTypeName);
432 m_inputTypeView = m_inputType;
433 ensureUserAgentShadowRoot();
435 updateTouchEventHandlerRegistry();
437 setNeedsWillValidateCheck();
439 m_inputType->warnIfValueIsInvalid(fastGetAttribute(valueAttr).string());
441 m_inputTypeView->updateView();
442 setTextAsOfLastFormControlChangeEvent(value());
443 setChangedSinceLastFormControlChangeEvent(false);
446 void HTMLInputElement::updateType()
448 ASSERT(m_inputType);
449 ASSERT(m_inputTypeView);
451 const AtomicString& newTypeName = InputType::normalizeTypeName(fastGetAttribute(typeAttr));
452 if (m_inputType->formControlType() == newTypeName)
453 return;
455 RefPtrWillBeRawPtr<InputType> newType = InputType::create(*this, newTypeName);
456 removeFromRadioButtonGroup();
458 bool didStoreValue = m_inputType->storesValueSeparateFromAttribute();
459 bool didRespectHeightAndWidth = m_inputType->shouldRespectHeightAndWidthAttributes();
461 m_inputTypeView->destroyShadowSubtree();
462 lazyReattachIfAttached();
464 m_inputType = newType.release();
465 if (openShadowRoot())
466 m_inputTypeView = InputTypeView::create(*this);
467 else
468 m_inputTypeView = m_inputType;
469 m_inputTypeView->createShadowSubtree();
471 updateTouchEventHandlerRegistry();
473 setNeedsWillValidateCheck();
475 bool willStoreValue = m_inputType->storesValueSeparateFromAttribute();
477 if (didStoreValue && !willStoreValue && hasDirtyValue()) {
478 setAttribute(valueAttr, AtomicString(m_valueIfDirty));
479 m_valueIfDirty = String();
481 if (!didStoreValue && willStoreValue) {
482 AtomicString valueString = fastGetAttribute(valueAttr);
483 m_inputType->warnIfValueIsInvalid(valueString);
484 m_valueIfDirty = sanitizeValue(valueString);
485 } else {
486 if (!hasDirtyValue())
487 m_inputType->warnIfValueIsInvalid(fastGetAttribute(valueAttr).string());
488 updateValueIfNeeded();
491 m_needsToUpdateViewValue = true;
492 m_inputTypeView->updateView();
494 if (didRespectHeightAndWidth != m_inputType->shouldRespectHeightAndWidthAttributes()) {
495 ASSERT(elementData());
496 AttributeCollection attributes = attributesWithoutUpdate();
497 if (const Attribute* height = attributes.find(heightAttr))
498 attributeChanged(heightAttr, height->value());
499 if (const Attribute* width = attributes.find(widthAttr))
500 attributeChanged(widthAttr, width->value());
501 if (const Attribute* align = attributes.find(alignAttr))
502 attributeChanged(alignAttr, align->value());
505 if (document().focusedElement() == this)
506 document().updateFocusAppearanceSoon(true /* restore selection */);
508 setTextAsOfLastFormControlChangeEvent(value());
509 setChangedSinceLastFormControlChangeEvent(false);
511 addToRadioButtonGroup();
513 setNeedsValidityCheck();
514 notifyFormStateChanged();
517 void HTMLInputElement::subtreeHasChanged()
519 m_inputTypeView->subtreeHasChanged();
520 // When typing in an input field, childrenChanged is not called, so we need to force the directionality check.
521 calculateAndAdjustDirectionality();
524 const AtomicString& HTMLInputElement::formControlType() const
526 return m_inputType->formControlType();
529 bool HTMLInputElement::shouldSaveAndRestoreFormControlState() const
531 if (!m_inputType->shouldSaveAndRestoreFormControlState())
532 return false;
533 return HTMLTextFormControlElement::shouldSaveAndRestoreFormControlState();
536 FormControlState HTMLInputElement::saveFormControlState() const
538 return m_inputType->saveFormControlState();
541 void HTMLInputElement::restoreFormControlState(const FormControlState& state)
543 m_inputType->restoreFormControlState(state);
544 m_stateRestored = true;
547 bool HTMLInputElement::canStartSelection() const
549 if (!isTextField())
550 return false;
551 return HTMLTextFormControlElement::canStartSelection();
554 int HTMLInputElement::selectionStartForBinding(ExceptionState& exceptionState) const
556 if (!m_inputType->supportsSelectionAPI()) {
557 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection.");
558 return 0;
560 return HTMLTextFormControlElement::selectionStart();
563 int HTMLInputElement::selectionEndForBinding(ExceptionState& exceptionState) const
565 if (!m_inputType->supportsSelectionAPI()) {
566 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection.");
567 return 0;
569 return HTMLTextFormControlElement::selectionEnd();
572 String HTMLInputElement::selectionDirectionForBinding(ExceptionState& exceptionState) const
574 if (!m_inputType->supportsSelectionAPI()) {
575 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection.");
576 return String();
578 return HTMLTextFormControlElement::selectionDirection();
581 void HTMLInputElement::setSelectionStartForBinding(int start, ExceptionState& exceptionState)
583 if (!m_inputType->supportsSelectionAPI()) {
584 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection.");
585 return;
587 HTMLTextFormControlElement::setSelectionStart(start);
590 void HTMLInputElement::setSelectionEndForBinding(int end, ExceptionState& exceptionState)
592 if (!m_inputType->supportsSelectionAPI()) {
593 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection.");
594 return;
596 HTMLTextFormControlElement::setSelectionEnd(end);
599 void HTMLInputElement::setSelectionDirectionForBinding(const String& direction, ExceptionState& exceptionState)
601 if (!m_inputType->supportsSelectionAPI()) {
602 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection.");
603 return;
605 HTMLTextFormControlElement::setSelectionDirection(direction);
608 void HTMLInputElement::setSelectionRangeForBinding(int start, int end, ExceptionState& exceptionState)
610 if (!m_inputType->supportsSelectionAPI()) {
611 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection.");
612 return;
614 HTMLTextFormControlElement::setSelectionRange(start, end);
617 void HTMLInputElement::setSelectionRangeForBinding(int start, int end, const String& direction, ExceptionState& exceptionState)
619 if (!m_inputType->supportsSelectionAPI()) {
620 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection.");
621 return;
623 HTMLTextFormControlElement::setSelectionRange(start, end, direction);
626 void HTMLInputElement::accessKeyAction(bool sendMouseEvents)
628 m_inputType->accessKeyAction(sendMouseEvents);
631 bool HTMLInputElement::isPresentationAttribute(const QualifiedName& name) const
633 // FIXME: Remove type check.
634 if (name == vspaceAttr || name == hspaceAttr || name == alignAttr || name == widthAttr || name == heightAttr || (name == borderAttr && type() == InputTypeNames::image))
635 return true;
636 return HTMLTextFormControlElement::isPresentationAttribute(name);
639 void HTMLInputElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
641 if (name == vspaceAttr) {
642 addHTMLLengthToStyle(style, CSSPropertyMarginTop, value);
643 addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value);
644 } else if (name == hspaceAttr) {
645 addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value);
646 addHTMLLengthToStyle(style, CSSPropertyMarginRight, value);
647 } else if (name == alignAttr) {
648 if (m_inputType->shouldRespectAlignAttribute())
649 applyAlignmentAttributeToStyle(value, style);
650 } else if (name == widthAttr) {
651 if (m_inputType->shouldRespectHeightAndWidthAttributes())
652 addHTMLLengthToStyle(style, CSSPropertyWidth, value);
653 } else if (name == heightAttr) {
654 if (m_inputType->shouldRespectHeightAndWidthAttributes())
655 addHTMLLengthToStyle(style, CSSPropertyHeight, value);
656 } else if (name == borderAttr && type() == InputTypeNames::image) { // FIXME: Remove type check.
657 applyBorderAttributeToStyle(value, style);
658 } else {
659 HTMLTextFormControlElement::collectStyleForPresentationAttribute(name, value, style);
663 void HTMLInputElement::attributeWillChange(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue)
665 if (name == formactionAttr && inDocument()) {
666 V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
667 if (activityLogger) {
668 Vector<String> argv;
669 argv.append("input");
670 argv.append(formactionAttr.toString());
671 argv.append(oldValue);
672 argv.append(newValue);
673 activityLogger->logEvent("blinkSetAttribute", argv.size(), argv.data());
676 HTMLTextFormControlElement::attributeWillChange(name, oldValue, newValue);
679 void HTMLInputElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
681 ASSERT(m_inputType);
682 ASSERT(m_inputTypeView);
684 if (name == nameAttr) {
685 removeFromRadioButtonGroup();
686 m_name = value;
687 addToRadioButtonGroup();
688 HTMLTextFormControlElement::parseAttribute(name, value);
689 } else if (name == autocompleteAttr) {
690 if (equalIgnoringCase(value, "off")) {
691 m_autocomplete = Off;
692 } else {
693 if (value.isEmpty())
694 m_autocomplete = Uninitialized;
695 else
696 m_autocomplete = On;
698 } else if (name == typeAttr) {
699 updateType();
700 } else if (name == valueAttr) {
701 // We only need to setChanged if the form is looking at the default value right now.
702 if (!hasDirtyValue()) {
703 updatePlaceholderVisibility(false);
704 setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::fromAttribute(valueAttr));
706 m_needsToUpdateViewValue = true;
707 setNeedsValidityCheck();
708 m_valueAttributeWasUpdatedAfterParsing = !m_parsingInProgress;
709 m_inputType->warnIfValueIsInvalidAndElementIsVisible(value);
710 m_inputTypeView->valueAttributeChanged();
711 } else if (name == checkedAttr) {
712 // Another radio button in the same group might be checked by state
713 // restore. We shouldn't call setChecked() even if this has the checked
714 // attribute. So, delay the setChecked() call until
715 // finishParsingChildren() is called if parsing is in progress.
716 if (!m_parsingInProgress && m_reflectsCheckedAttribute) {
717 setChecked(!value.isNull());
718 m_reflectsCheckedAttribute = true;
720 } else if (name == maxlengthAttr) {
721 parseMaxLengthAttribute(value);
722 } else if (name == minlengthAttr) {
723 parseMinLengthAttribute(value);
724 } else if (name == sizeAttr) {
725 int oldSize = m_size;
726 m_size = defaultSize;
727 int valueAsInteger;
728 if (!value.isEmpty() && parseHTMLInteger(value, valueAsInteger) && valueAsInteger > 0)
729 m_size = valueAsInteger;
730 if (m_size != oldSize && layoutObject())
731 layoutObject()->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(LayoutInvalidationReason::AttributeChanged);
732 } else if (name == altAttr) {
733 m_inputTypeView->altAttributeChanged();
734 } else if (name == srcAttr) {
735 m_inputTypeView->srcAttributeChanged();
736 } else if (name == usemapAttr || name == accesskeyAttr) {
737 // FIXME: ignore for the moment
738 } else if (name == onsearchAttr) {
739 // Search field and slider attributes all just cause updateFromElement to be called through style recalcing.
740 setAttributeEventListener(EventTypeNames::search, createAttributeEventListener(this, name, value, eventParameterName()));
741 } else if (name == resultsAttr) {
742 int oldResults = m_maxResults;
743 m_maxResults = !value.isNull() ? std::min(value.toInt(), maxSavedResults) : -1;
744 // FIXME: Detaching just for maxResults change is not ideal. We should figure out the right
745 // time to relayout for this change.
746 if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0))
747 lazyReattachIfAttached();
748 setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::fromAttribute(resultsAttr));
749 UseCounter::count(document(), UseCounter::ResultsAttribute);
750 } else if (name == incrementalAttr) {
751 setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::fromAttribute(incrementalAttr));
752 UseCounter::count(document(), UseCounter::IncrementalAttribute);
753 } else if (name == minAttr) {
754 m_inputTypeView->minOrMaxAttributeChanged();
755 m_inputType->sanitizeValueInResponseToMinOrMaxAttributeChange();
756 setNeedsValidityCheck();
757 UseCounter::count(document(), UseCounter::MinAttribute);
758 } else if (name == maxAttr) {
759 m_inputTypeView->minOrMaxAttributeChanged();
760 m_inputType->sanitizeValueInResponseToMinOrMaxAttributeChange();
761 setNeedsValidityCheck();
762 UseCounter::count(document(), UseCounter::MaxAttribute);
763 } else if (name == multipleAttr) {
764 m_inputTypeView->multipleAttributeChanged();
765 setNeedsValidityCheck();
766 } else if (name == stepAttr) {
767 m_inputTypeView->stepAttributeChanged();
768 setNeedsValidityCheck();
769 UseCounter::count(document(), UseCounter::StepAttribute);
770 } else if (name == patternAttr) {
771 setNeedsValidityCheck();
772 UseCounter::count(document(), UseCounter::PatternAttribute);
773 } else if (name == disabledAttr) {
774 HTMLTextFormControlElement::parseAttribute(name, value);
775 m_inputTypeView->disabledAttributeChanged();
776 } else if (name == readonlyAttr) {
777 HTMLTextFormControlElement::parseAttribute(name, value);
778 m_inputTypeView->readonlyAttributeChanged();
779 } else if (name == listAttr) {
780 m_hasNonEmptyList = !value.isEmpty();
781 if (m_hasNonEmptyList) {
782 resetListAttributeTargetObserver();
783 listAttributeTargetChanged();
785 UseCounter::count(document(), UseCounter::ListAttribute);
786 } else if (name == webkitdirectoryAttr) {
787 HTMLTextFormControlElement::parseAttribute(name, value);
788 UseCounter::count(document(), UseCounter::PrefixedDirectoryAttribute);
789 } else {
790 HTMLTextFormControlElement::parseAttribute(name, value);
792 m_inputTypeView->attributeChanged();
795 void HTMLInputElement::parserDidSetAttributes()
797 ASSERT(m_parsingInProgress);
798 initializeTypeInParsing();
801 void HTMLInputElement::finishParsingChildren()
803 m_parsingInProgress = false;
804 ASSERT(m_inputType);
805 ASSERT(m_inputTypeView);
806 HTMLTextFormControlElement::finishParsingChildren();
807 if (!m_stateRestored) {
808 bool checked = hasAttribute(checkedAttr);
809 if (checked)
810 setChecked(checked);
811 m_reflectsCheckedAttribute = true;
815 bool HTMLInputElement::layoutObjectIsNeeded(const ComputedStyle& style)
817 return m_inputType->layoutObjectIsNeeded() && HTMLTextFormControlElement::layoutObjectIsNeeded(style);
820 LayoutObject* HTMLInputElement::createLayoutObject(const ComputedStyle& style)
822 return m_inputTypeView->createLayoutObject(style);
825 void HTMLInputElement::attach(const AttachContext& context)
827 HTMLTextFormControlElement::attach(context);
829 m_inputTypeView->startResourceLoading();
830 m_inputType->countUsage();
832 if (document().focusedElement() == this)
833 document().updateFocusAppearanceSoon(true /* restore selection */);
836 void HTMLInputElement::detach(const AttachContext& context)
838 HTMLTextFormControlElement::detach(context);
839 m_needsToUpdateViewValue = true;
840 m_inputTypeView->closePopupView();
843 String HTMLInputElement::altText() const
845 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
846 // also heavily discussed by Hixie on bugzilla
847 // note this is intentionally different to HTMLImageElement::altText()
848 String alt = fastGetAttribute(altAttr);
849 // fall back to title attribute
850 if (alt.isNull())
851 alt = fastGetAttribute(titleAttr);
852 if (alt.isNull())
853 alt = fastGetAttribute(valueAttr);
854 if (alt.isEmpty())
855 alt = locale().queryString(WebLocalizedString::InputElementAltText);
856 return alt;
859 bool HTMLInputElement::canBeSuccessfulSubmitButton() const
861 return m_inputType->canBeSuccessfulSubmitButton();
864 bool HTMLInputElement::isActivatedSubmit() const
866 return m_isActivatedSubmit;
869 void HTMLInputElement::setActivatedSubmit(bool flag)
871 m_isActivatedSubmit = flag;
874 bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart)
876 return m_inputType->isFormDataAppendable() && m_inputType->appendFormData(encoding, multipart);
879 String HTMLInputElement::resultForDialogSubmit()
881 return m_inputType->resultForDialogSubmit();
884 void HTMLInputElement::resetImpl()
886 if (m_inputType->storesValueSeparateFromAttribute()) {
887 setValue(String());
888 setNeedsValidityCheck();
891 setChecked(hasAttribute(checkedAttr));
892 m_reflectsCheckedAttribute = true;
895 bool HTMLInputElement::isTextField() const
897 return m_inputType->isTextField();
900 void HTMLInputElement::dispatchChangeEventIfNeeded()
902 if (inDocument() && m_inputType->shouldSendChangeEventAfterCheckedChanged())
903 dispatchFormControlChangeEvent();
906 bool HTMLInputElement::checked() const
908 m_inputType->readingChecked();
909 return m_isChecked;
912 void HTMLInputElement::setChecked(bool nowChecked, TextFieldEventBehavior eventBehavior)
914 if (checked() == nowChecked)
915 return;
917 RefPtrWillBeRawPtr<HTMLInputElement> protector(this);
918 m_reflectsCheckedAttribute = false;
919 m_isChecked = nowChecked;
921 if (RadioButtonGroupScope* scope = radioButtonGroupScope())
922 scope->updateCheckedState(this);
923 if (layoutObject())
924 LayoutTheme::theme().controlStateChanged(*layoutObject(), CheckedControlState);
926 setNeedsValidityCheck();
928 // Ideally we'd do this from the layout tree (matching
929 // LayoutTextView), but it's not possible to do it at the moment
930 // because of the way the code is structured.
931 if (layoutObject()) {
932 if (AXObjectCache* cache = layoutObject()->document().existingAXObjectCache())
933 cache->checkedStateChanged(this);
936 // Only send a change event for items in the document (avoid firing during
937 // parsing) and don't send a change event for a radio button that's getting
938 // unchecked to match other browsers. DOM is not a useful standard for this
939 // because it says only to fire change events at "lose focus" time, which is
940 // definitely wrong in practice for these types of elements.
941 if (eventBehavior != DispatchNoEvent && inDocument() && m_inputType->shouldSendChangeEventAfterCheckedChanged()) {
942 setTextAsOfLastFormControlChangeEvent(String());
943 if (eventBehavior == DispatchInputAndChangeEvent)
944 dispatchFormControlInputEvent();
947 pseudoStateChanged(CSSSelector::PseudoChecked);
950 void HTMLInputElement::setIndeterminate(bool newValue)
952 if (indeterminate() == newValue)
953 return;
955 m_isIndeterminate = newValue;
957 pseudoStateChanged(CSSSelector::PseudoIndeterminate);
959 if (layoutObject())
960 LayoutTheme::theme().controlStateChanged(*layoutObject(), CheckedControlState);
963 int HTMLInputElement::size() const
965 return m_size;
968 bool HTMLInputElement::sizeShouldIncludeDecoration(int& preferredSize) const
970 return m_inputTypeView->sizeShouldIncludeDecoration(defaultSize, preferredSize);
973 void HTMLInputElement::copyNonAttributePropertiesFromElement(const Element& source)
975 const HTMLInputElement& sourceElement = static_cast<const HTMLInputElement&>(source);
977 m_valueIfDirty = sourceElement.m_valueIfDirty;
978 setChecked(sourceElement.m_isChecked);
979 m_reflectsCheckedAttribute = sourceElement.m_reflectsCheckedAttribute;
980 m_isIndeterminate = sourceElement.m_isIndeterminate;
982 HTMLTextFormControlElement::copyNonAttributePropertiesFromElement(source);
984 m_needsToUpdateViewValue = true;
985 m_inputTypeView->updateView();
988 String HTMLInputElement::value() const
990 String value;
991 if (m_inputType->getTypeSpecificValue(value))
992 return value;
994 value = m_valueIfDirty;
995 if (!value.isNull())
996 return value;
998 AtomicString valueString = fastGetAttribute(valueAttr);
999 value = sanitizeValue(valueString);
1000 if (!value.isNull())
1001 return value;
1003 return m_inputType->fallbackValue();
1006 String HTMLInputElement::valueWithDefault() const
1008 String value = this->value();
1009 if (!value.isNull())
1010 return value;
1012 return m_inputType->defaultValue();
1015 void HTMLInputElement::setValueForUser(const String& value)
1017 // Call setValue and make it send a change event.
1018 setValue(value, DispatchChangeEvent);
1021 const String& HTMLInputElement::suggestedValue() const
1023 return m_suggestedValue;
1026 void HTMLInputElement::setSuggestedValue(const String& value)
1028 if (!m_inputType->canSetSuggestedValue())
1029 return;
1030 m_needsToUpdateViewValue = true;
1031 m_suggestedValue = sanitizeValue(value);
1032 setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::ControlValue));
1033 m_inputTypeView->updateView();
1036 void HTMLInputElement::setEditingValue(const String& value)
1038 if (!layoutObject() || !isTextField())
1039 return;
1040 setInnerEditorValue(value);
1041 subtreeHasChanged();
1043 unsigned max = value.length();
1044 if (focused())
1045 setSelectionRange(max, max, SelectionHasNoDirection, NotDispatchSelectEvent);
1046 else
1047 cacheSelectionInResponseToSetValue(max);
1049 dispatchInputEvent();
1052 void HTMLInputElement::setInnerEditorValue(const String& value)
1054 HTMLTextFormControlElement::setInnerEditorValue(value);
1055 m_needsToUpdateViewValue = false;
1058 void HTMLInputElement::setValue(const String& value, ExceptionState& exceptionState, TextFieldEventBehavior eventBehavior)
1060 // FIXME: Remove type check.
1061 if (type() == InputTypeNames::file && !value.isEmpty()) {
1062 exceptionState.throwDOMException(InvalidStateError, "This input element accepts a filename, which may only be programmatically set to the empty string.");
1063 return;
1065 setValue(value, eventBehavior);
1068 void HTMLInputElement::setValue(const String& value, TextFieldEventBehavior eventBehavior)
1070 m_inputType->warnIfValueIsInvalidAndElementIsVisible(value);
1071 if (!m_inputType->canSetValue(value))
1072 return;
1074 RefPtrWillBeRawPtr<HTMLInputElement> protector(this);
1075 EventQueueScope scope;
1076 String sanitizedValue = sanitizeValue(value);
1077 bool valueChanged = sanitizedValue != this->value();
1079 setLastChangeWasNotUserEdit();
1080 m_needsToUpdateViewValue = true;
1081 m_suggestedValue = String(); // Prevent TextFieldInputType::setValue from using the suggested value.
1083 m_inputType->setValue(sanitizedValue, valueChanged, eventBehavior);
1085 if (valueChanged && eventBehavior == DispatchNoEvent)
1086 setTextAsOfLastFormControlChangeEvent(sanitizedValue.isNull() ? defaultValue() : sanitizedValue);
1088 if (!valueChanged)
1089 return;
1091 notifyFormStateChanged();
1094 void HTMLInputElement::setValueInternal(const String& sanitizedValue, TextFieldEventBehavior eventBehavior)
1096 m_valueIfDirty = sanitizedValue;
1097 setNeedsValidityCheck();
1098 if (document().focusedElement() == this)
1099 document().frameHost()->chromeClient().didUpdateTextOfFocusedElementByNonUserInput();
1102 void HTMLInputElement::updateView()
1104 m_inputTypeView->updateView();
1107 double HTMLInputElement::valueAsDate(bool& isNull) const
1109 double date = m_inputType->valueAsDate();
1110 isNull = !std::isfinite(date);
1111 return date;
1114 void HTMLInputElement::setValueAsDate(double value, ExceptionState& exceptionState)
1116 m_inputType->setValueAsDate(value, exceptionState);
1119 double HTMLInputElement::valueAsNumber() const
1121 return m_inputType->valueAsDouble();
1124 void HTMLInputElement::setValueAsNumber(double newValue, ExceptionState& exceptionState, TextFieldEventBehavior eventBehavior)
1126 // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-input-element-attributes.html#dom-input-valueasnumber
1127 // On setting, if the new value is infinite, then throw a TypeError exception.
1128 if (std::isinf(newValue)) {
1129 exceptionState.throwTypeError(ExceptionMessages::notAFiniteNumber(newValue));
1130 return;
1132 m_inputType->setValueAsDouble(newValue, eventBehavior, exceptionState);
1135 void HTMLInputElement::setValueFromRenderer(const String& value)
1137 // File upload controls will never use this.
1138 ASSERT(type() != InputTypeNames::file);
1140 m_suggestedValue = String();
1142 // Renderer and our event handler are responsible for sanitizing values.
1143 ASSERT(value == sanitizeValue(value) || sanitizeValue(value).isEmpty());
1145 m_valueIfDirty = value;
1146 m_needsToUpdateViewValue = false;
1148 // Input event is fired by the Node::defaultEventHandler for editable controls.
1149 if (!isTextField())
1150 dispatchInputEvent();
1151 notifyFormStateChanged();
1153 setNeedsValidityCheck();
1155 // Clear autofill flag (and yellow background) on user edit.
1156 setAutofilled(false);
1159 void* HTMLInputElement::preDispatchEventHandler(Event* event)
1161 if (event->type() == EventTypeNames::textInput && m_inputTypeView->shouldSubmitImplicitly(event)) {
1162 event->stopPropagation();
1163 return nullptr;
1165 if (event->type() != EventTypeNames::click)
1166 return nullptr;
1167 if (!event->isMouseEvent() || toMouseEvent(event)->button() != LeftButton)
1168 return nullptr;
1169 #if ENABLE(OILPAN)
1170 return m_inputTypeView->willDispatchClick();
1171 #else
1172 // FIXME: Check whether there are any cases where this actually ends up leaking.
1173 return m_inputTypeView->willDispatchClick().leakPtr();
1174 #endif
1177 void HTMLInputElement::postDispatchEventHandler(Event* event, void* dataFromPreDispatch)
1179 OwnPtrWillBeRawPtr<ClickHandlingState> state = adoptPtrWillBeNoop(static_cast<ClickHandlingState*>(dataFromPreDispatch));
1180 if (!state)
1181 return;
1182 m_inputTypeView->didDispatchClick(event, *state);
1185 void HTMLInputElement::defaultEventHandler(Event* evt)
1187 if (evt->isMouseEvent() && evt->type() == EventTypeNames::click && toMouseEvent(evt)->button() == LeftButton) {
1188 m_inputTypeView->handleClickEvent(toMouseEvent(evt));
1189 if (evt->defaultHandled())
1190 return;
1193 if (evt->isTouchEvent() && m_inputTypeView->hasTouchEventHandler()) {
1194 m_inputTypeView->handleTouchEvent(toTouchEvent(evt));
1195 if (evt->defaultHandled())
1196 return;
1199 if (evt->isKeyboardEvent() && evt->type() == EventTypeNames::keydown) {
1200 m_inputTypeView->handleKeydownEvent(toKeyboardEvent(evt));
1201 if (evt->defaultHandled())
1202 return;
1205 // Call the base event handler before any of our own event handling for almost all events in text fields.
1206 // Makes editing keyboard handling take precedence over the keydown and keypress handling in this function.
1207 bool callBaseClassEarly = isTextField() && (evt->type() == EventTypeNames::keydown || evt->type() == EventTypeNames::keypress);
1208 if (callBaseClassEarly) {
1209 HTMLTextFormControlElement::defaultEventHandler(evt);
1210 if (evt->defaultHandled())
1211 return;
1214 // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means
1215 // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks
1216 // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element
1217 // must dispatch a DOMActivate event - a click event will not do the job.
1218 if (evt->type() == EventTypeNames::DOMActivate) {
1219 m_inputType->handleDOMActivateEvent(evt);
1220 if (evt->defaultHandled())
1221 return;
1224 // Use key press event here since sending simulated mouse events
1225 // on key down blocks the proper sending of the key press event.
1226 if (evt->isKeyboardEvent() && evt->type() == EventTypeNames::keypress) {
1227 m_inputTypeView->handleKeypressEvent(toKeyboardEvent(evt));
1228 if (evt->defaultHandled())
1229 return;
1232 if (evt->isKeyboardEvent() && evt->type() == EventTypeNames::keyup) {
1233 m_inputTypeView->handleKeyupEvent(toKeyboardEvent(evt));
1234 if (evt->defaultHandled())
1235 return;
1238 if (m_inputTypeView->shouldSubmitImplicitly(evt)) {
1239 // FIXME: Remove type check.
1240 if (type() == InputTypeNames::search)
1241 onSearch();
1242 // Form submission finishes editing, just as loss of focus does.
1243 // If there was a change, send the event now.
1244 if (wasChangedSinceLastFormControlChangeEvent())
1245 dispatchFormControlChangeEvent();
1247 RefPtrWillBeRawPtr<HTMLFormElement> formForSubmission = m_inputTypeView->formForSubmission();
1248 // Form may never have been present, or may have been destroyed by code responding to the change event.
1249 if (formForSubmission)
1250 formForSubmission->submitImplicitly(evt, canTriggerImplicitSubmission());
1252 evt->setDefaultHandled();
1253 return;
1256 if (evt->isBeforeTextInsertedEvent())
1257 m_inputTypeView->handleBeforeTextInsertedEvent(static_cast<BeforeTextInsertedEvent*>(evt));
1259 if (evt->isMouseEvent() && evt->type() == EventTypeNames::mousedown) {
1260 m_inputTypeView->handleMouseDownEvent(toMouseEvent(evt));
1261 if (evt->defaultHandled())
1262 return;
1265 m_inputTypeView->forwardEvent(evt);
1267 if (!callBaseClassEarly && !evt->defaultHandled())
1268 HTMLTextFormControlElement::defaultEventHandler(evt);
1271 bool HTMLInputElement::willRespondToMouseClickEvents()
1273 // FIXME: Consider implementing willRespondToMouseClickEvents() in InputType if more accurate results are necessary.
1274 if (!isDisabledFormControl())
1275 return true;
1277 return HTMLTextFormControlElement::willRespondToMouseClickEvents();
1280 bool HTMLInputElement::isURLAttribute(const Attribute& attribute) const
1282 return attribute.name() == srcAttr || attribute.name() == formactionAttr || HTMLTextFormControlElement::isURLAttribute(attribute);
1285 bool HTMLInputElement::hasLegalLinkAttribute(const QualifiedName& name) const
1287 return m_inputType->hasLegalLinkAttribute(name) || HTMLTextFormControlElement::hasLegalLinkAttribute(name);
1290 const QualifiedName& HTMLInputElement::subResourceAttributeName() const
1292 return m_inputType->subResourceAttributeName();
1295 const AtomicString& HTMLInputElement::defaultValue() const
1297 return fastGetAttribute(valueAttr);
1300 static inline bool isRFC2616TokenCharacter(UChar ch)
1302 return isASCII(ch) && ch > ' ' && ch != '"' && ch != '(' && ch != ')' && ch != ',' && ch != '/' && (ch < ':' || ch > '@') && (ch < '[' || ch > ']') && ch != '{' && ch != '}' && ch != 0x7f;
1305 static bool isValidMIMEType(const String& type)
1307 size_t slashPosition = type.find('/');
1308 if (slashPosition == kNotFound || !slashPosition || slashPosition == type.length() - 1)
1309 return false;
1310 for (size_t i = 0; i < type.length(); ++i) {
1311 if (!isRFC2616TokenCharacter(type[i]) && i != slashPosition)
1312 return false;
1314 return true;
1317 static bool isValidFileExtension(const String& type)
1319 if (type.length() < 2)
1320 return false;
1321 return type[0] == '.';
1324 static Vector<String> parseAcceptAttribute(const String& acceptString, bool (*predicate)(const String&))
1326 Vector<String> types;
1327 if (acceptString.isEmpty())
1328 return types;
1330 Vector<String> splitTypes;
1331 acceptString.split(',', false, splitTypes);
1332 for (const String& splitType : splitTypes) {
1333 String trimmedType = stripLeadingAndTrailingHTMLSpaces(splitType);
1334 if (trimmedType.isEmpty())
1335 continue;
1336 if (!predicate(trimmedType))
1337 continue;
1338 types.append(trimmedType.lower());
1341 return types;
1344 Vector<String> HTMLInputElement::acceptMIMETypes()
1346 return parseAcceptAttribute(fastGetAttribute(acceptAttr), isValidMIMEType);
1349 Vector<String> HTMLInputElement::acceptFileExtensions()
1351 return parseAcceptAttribute(fastGetAttribute(acceptAttr), isValidFileExtension);
1354 const AtomicString& HTMLInputElement::alt() const
1356 return fastGetAttribute(altAttr);
1359 int HTMLInputElement::maxLength() const
1361 return m_maxLength;
1364 int HTMLInputElement::minLength() const
1366 return m_minLength;
1369 void HTMLInputElement::setMaxLength(int maxLength, ExceptionState& exceptionState)
1371 if (maxLength < 0)
1372 exceptionState.throwDOMException(IndexSizeError, "The value provided (" + String::number(maxLength) + ") is negative.");
1373 else if (maxLength < m_minLength)
1374 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMinimumBound("maxLength", maxLength, m_minLength));
1375 else
1376 setIntegralAttribute(maxlengthAttr, maxLength);
1379 void HTMLInputElement::setMinLength(int minLength, ExceptionState& exceptionState)
1381 if (minLength < 0)
1382 exceptionState.throwDOMException(IndexSizeError, "The value provided (" + String::number(minLength) + ") is negative.");
1383 else if (minLength > m_maxLength)
1384 exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("minLength", minLength, m_maxLength));
1385 else
1386 setIntegralAttribute(minlengthAttr, minLength);
1389 bool HTMLInputElement::multiple() const
1391 return fastHasAttribute(multipleAttr);
1394 void HTMLInputElement::setSize(unsigned size)
1396 setUnsignedIntegralAttribute(sizeAttr, size);
1399 void HTMLInputElement::setSize(unsigned size, ExceptionState& exceptionState)
1401 if (!size)
1402 exceptionState.throwDOMException(IndexSizeError, "The value provided is 0, which is an invalid size.");
1403 else
1404 setSize(size);
1407 KURL HTMLInputElement::src() const
1409 return document().completeURL(fastGetAttribute(srcAttr));
1412 FileList* HTMLInputElement::files()
1414 return m_inputType->files();
1417 void HTMLInputElement::setFiles(FileList* files)
1419 m_inputType->setFiles(files);
1422 bool HTMLInputElement::receiveDroppedFiles(const DragData* dragData)
1424 return m_inputType->receiveDroppedFiles(dragData);
1427 String HTMLInputElement::droppedFileSystemId()
1429 return m_inputType->droppedFileSystemId();
1432 bool HTMLInputElement::canReceiveDroppedFiles() const
1434 return m_canReceiveDroppedFiles;
1437 void HTMLInputElement::setCanReceiveDroppedFiles(bool canReceiveDroppedFiles)
1439 if (!!m_canReceiveDroppedFiles == canReceiveDroppedFiles)
1440 return;
1441 m_canReceiveDroppedFiles = canReceiveDroppedFiles;
1442 if (layoutObject())
1443 layoutObject()->updateFromElement();
1446 String HTMLInputElement::sanitizeValue(const String& proposedValue) const
1448 if (proposedValue.isNull())
1449 return proposedValue;
1450 return m_inputType->sanitizeValue(proposedValue);
1453 String HTMLInputElement::localizeValue(const String& proposedValue) const
1455 if (proposedValue.isNull())
1456 return proposedValue;
1457 return m_inputType->localizeValue(proposedValue);
1460 bool HTMLInputElement::isInRange() const
1462 return m_inputType->isInRange(value());
1465 bool HTMLInputElement::isOutOfRange() const
1467 return m_inputType->isOutOfRange(value());
1470 bool HTMLInputElement::isRequiredFormControl() const
1472 return m_inputType->supportsRequired() && isRequired();
1475 bool HTMLInputElement::matchesReadOnlyPseudoClass() const
1477 return m_inputType->supportsReadOnly() && isReadOnly();
1480 bool HTMLInputElement::matchesReadWritePseudoClass() const
1482 return m_inputType->supportsReadOnly() && !isReadOnly();
1485 void HTMLInputElement::onSearch()
1487 // FIXME: Remove type check, and static_cast.
1488 ASSERT(type() == InputTypeNames::search);
1489 if (m_inputType)
1490 static_cast<SearchInputType*>(m_inputType.get())->stopSearchEventTimer();
1491 dispatchEvent(Event::createBubble(EventTypeNames::search));
1494 void HTMLInputElement::updateClearButtonVisibility()
1496 m_inputTypeView->updateClearButtonVisibility();
1499 void HTMLInputElement::willChangeForm()
1501 removeFromRadioButtonGroup();
1502 HTMLTextFormControlElement::willChangeForm();
1505 void HTMLInputElement::didChangeForm()
1507 HTMLTextFormControlElement::didChangeForm();
1508 addToRadioButtonGroup();
1511 Node::InsertionNotificationRequest HTMLInputElement::insertedInto(ContainerNode* insertionPoint)
1513 if (insertionPoint->inDocument()) {
1514 V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
1515 if (activityLogger) {
1516 Vector<String> argv;
1517 argv.append("input");
1518 argv.append(fastGetAttribute(typeAttr));
1519 argv.append(fastGetAttribute(formactionAttr));
1520 activityLogger->logEvent("blinkAddElement", argv.size(), argv.data());
1523 HTMLTextFormControlElement::insertedInto(insertionPoint);
1524 if (insertionPoint->inDocument() && !form())
1525 addToRadioButtonGroup();
1526 resetListAttributeTargetObserver();
1527 return InsertionShouldCallDidNotifySubtreeInsertions;
1530 void HTMLInputElement::removedFrom(ContainerNode* insertionPoint)
1532 m_inputTypeView->closePopupView();
1533 if (insertionPoint->inDocument() && !form())
1534 removeFromRadioButtonGroup();
1535 HTMLTextFormControlElement::removedFrom(insertionPoint);
1536 ASSERT(!inDocument());
1537 resetListAttributeTargetObserver();
1540 void HTMLInputElement::didMoveToNewDocument(Document& oldDocument)
1542 if (imageLoader())
1543 imageLoader()->elementDidMoveToNewDocument();
1545 // FIXME: Remove type check.
1546 if (type() == InputTypeNames::radio)
1547 oldDocument.formController().radioButtonGroupScope().removeButton(this);
1549 HTMLTextFormControlElement::didMoveToNewDocument(oldDocument);
1552 void HTMLInputElement::removeAllEventListeners()
1554 HTMLTextFormControlElement::removeAllEventListeners();
1555 m_hasTouchEventHandler = false;
1558 bool HTMLInputElement::recalcWillValidate() const
1560 return m_inputType->supportsValidation() && HTMLTextFormControlElement::recalcWillValidate();
1563 void HTMLInputElement::requiredAttributeChanged()
1565 HTMLTextFormControlElement::requiredAttributeChanged();
1566 if (RadioButtonGroupScope* scope = radioButtonGroupScope())
1567 scope->requiredAttributeChanged(this);
1568 m_inputTypeView->requiredAttributeChanged();
1571 void HTMLInputElement::selectColorInColorChooser(const Color& color)
1573 if (ColorChooserClient* client = m_inputType->colorChooserClient())
1574 client->didChooseColor(color);
1577 void HTMLInputElement::endColorChooser()
1579 if (ColorChooserClient* client = m_inputType->colorChooserClient())
1580 client->didEndChooser();
1583 HTMLElement* HTMLInputElement::list() const
1585 return dataList();
1588 HTMLDataListElement* HTMLInputElement::dataList() const
1590 if (!m_hasNonEmptyList)
1591 return nullptr;
1593 if (!m_inputType->shouldRespectListAttribute())
1594 return nullptr;
1596 Element* element = treeScope().getElementById(fastGetAttribute(listAttr));
1597 if (!element)
1598 return nullptr;
1599 if (!isHTMLDataListElement(*element))
1600 return nullptr;
1602 return toHTMLDataListElement(element);
1605 bool HTMLInputElement::hasValidDataListOptions() const
1607 HTMLDataListElement* dataList = this->dataList();
1608 if (!dataList)
1609 return false;
1610 RefPtrWillBeRawPtr<HTMLDataListOptionsCollection> options = dataList->options();
1611 for (unsigned i = 0; HTMLOptionElement* option = options->item(i); ++i) {
1612 if (isValidValue(option->value()))
1613 return true;
1615 return false;
1618 void HTMLInputElement::setListAttributeTargetObserver(PassOwnPtrWillBeRawPtr<ListAttributeTargetObserver> newObserver)
1620 if (m_listAttributeTargetObserver)
1621 m_listAttributeTargetObserver->unregister();
1622 m_listAttributeTargetObserver = newObserver;
1625 void HTMLInputElement::resetListAttributeTargetObserver()
1627 if (inDocument())
1628 setListAttributeTargetObserver(ListAttributeTargetObserver::create(fastGetAttribute(listAttr), this));
1629 else
1630 setListAttributeTargetObserver(nullptr);
1633 void HTMLInputElement::listAttributeTargetChanged()
1635 m_inputTypeView->listAttributeTargetChanged();
1638 bool HTMLInputElement::isSteppable() const
1640 return m_inputType->isSteppable();
1643 bool HTMLInputElement::isTextButton() const
1645 return m_inputType->isTextButton();
1648 bool HTMLInputElement::isImage() const
1650 return m_inputType->isImage();
1653 bool HTMLInputElement::isEnumeratable() const
1655 return m_inputType->isEnumeratable();
1658 bool HTMLInputElement::supportLabels() const
1660 return m_inputType->isInteractiveContent();
1663 bool HTMLInputElement::shouldAppearChecked() const
1665 return checked() && m_inputType->isCheckable();
1668 bool HTMLInputElement::supportsPlaceholder() const
1670 return m_inputType->supportsPlaceholder();
1673 void HTMLInputElement::updatePlaceholderText()
1675 return m_inputTypeView->updatePlaceholderText();
1678 void HTMLInputElement::parseMaxLengthAttribute(const AtomicString& value)
1680 int maxLength;
1681 if (!parseHTMLInteger(value, maxLength))
1682 maxLength = maximumLength;
1683 if (maxLength < 0 || maxLength > maximumLength)
1684 maxLength = maximumLength;
1685 int oldMaxLength = m_maxLength;
1686 m_maxLength = maxLength;
1687 if (oldMaxLength != maxLength)
1688 updateValueIfNeeded();
1689 setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::fromAttribute(maxlengthAttr));
1690 setNeedsValidityCheck();
1693 void HTMLInputElement::parseMinLengthAttribute(const AtomicString& value)
1695 int minLength;
1696 if (!parseHTMLInteger(value, minLength))
1697 minLength = 0;
1698 if (minLength < 0)
1699 minLength = 0;
1700 m_minLength = minLength;
1701 setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::fromAttribute(minlengthAttr));
1702 setNeedsValidityCheck();
1705 void HTMLInputElement::updateValueIfNeeded()
1707 String newValue = sanitizeValue(m_valueIfDirty);
1708 ASSERT(!m_valueIfDirty.isNull() || newValue.isNull());
1709 if (newValue != m_valueIfDirty)
1710 setValue(newValue);
1713 bool HTMLInputElement::supportsAutocapitalize() const
1715 return m_inputType->supportsAutocapitalize();
1718 const AtomicString& HTMLInputElement::defaultAutocapitalize() const
1720 return m_inputType->defaultAutocapitalize();
1723 String HTMLInputElement::defaultToolTip() const
1725 return m_inputType->defaultToolTip();
1728 bool HTMLInputElement::shouldAppearIndeterminate() const
1730 return m_inputType->shouldAppearIndeterminate();
1733 bool HTMLInputElement::isInRequiredRadioButtonGroup()
1735 // FIXME: Remove type check.
1736 ASSERT(type() == InputTypeNames::radio);
1737 if (RadioButtonGroupScope* scope = radioButtonGroupScope())
1738 return scope->isInRequiredGroup(this);
1739 return false;
1742 HTMLInputElement* HTMLInputElement::checkedRadioButtonForGroup()
1744 if (checked())
1745 return this;
1746 if (RadioButtonGroupScope* scope = radioButtonGroupScope())
1747 return scope->checkedButtonForGroup(name());
1748 return nullptr;
1751 RadioButtonGroupScope* HTMLInputElement::radioButtonGroupScope() const
1753 // FIXME: Remove type check.
1754 if (type() != InputTypeNames::radio)
1755 return nullptr;
1756 if (HTMLFormElement* formElement = form())
1757 return &formElement->radioButtonGroupScope();
1758 if (inDocument())
1759 return &document().formController().radioButtonGroupScope();
1760 return nullptr;
1763 inline void HTMLInputElement::addToRadioButtonGroup()
1765 if (RadioButtonGroupScope* scope = radioButtonGroupScope())
1766 scope->addButton(this);
1769 inline void HTMLInputElement::removeFromRadioButtonGroup()
1771 if (RadioButtonGroupScope* scope = radioButtonGroupScope())
1772 scope->removeButton(this);
1775 unsigned HTMLInputElement::height() const
1777 return m_inputType->height();
1780 unsigned HTMLInputElement::width() const
1782 return m_inputType->width();
1785 void HTMLInputElement::setHeight(unsigned height)
1787 setUnsignedIntegralAttribute(heightAttr, height);
1790 void HTMLInputElement::setWidth(unsigned width)
1792 setUnsignedIntegralAttribute(widthAttr, width);
1795 PassOwnPtrWillBeRawPtr<ListAttributeTargetObserver> ListAttributeTargetObserver::create(const AtomicString& id, HTMLInputElement* element)
1797 return adoptPtrWillBeNoop(new ListAttributeTargetObserver(id, element));
1800 ListAttributeTargetObserver::ListAttributeTargetObserver(const AtomicString& id, HTMLInputElement* element)
1801 : IdTargetObserver(element->treeScope().idTargetObserverRegistry(), id)
1802 , m_element(element)
1806 DEFINE_TRACE(ListAttributeTargetObserver)
1808 visitor->trace(m_element);
1809 IdTargetObserver::trace(visitor);
1812 void ListAttributeTargetObserver::idTargetChanged()
1814 m_element->listAttributeTargetChanged();
1817 void HTMLInputElement::setRangeText(const String& replacement, ExceptionState& exceptionState)
1819 if (!m_inputType->supportsSelectionAPI()) {
1820 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection.");
1821 return;
1824 HTMLTextFormControlElement::setRangeText(replacement, exceptionState);
1827 void HTMLInputElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode, ExceptionState& exceptionState)
1829 if (!m_inputType->supportsSelectionAPI()) {
1830 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection.");
1831 return;
1834 HTMLTextFormControlElement::setRangeText(replacement, start, end, selectionMode, exceptionState);
1837 bool HTMLInputElement::setupDateTimeChooserParameters(DateTimeChooserParameters& parameters)
1839 if (!document().view())
1840 return false;
1842 parameters.type = type();
1843 parameters.minimum = minimum();
1844 parameters.maximum = maximum();
1845 parameters.required = isRequired();
1846 if (!RuntimeEnabledFeatures::langAttributeAwareFormControlUIEnabled()) {
1847 parameters.locale = defaultLanguage();
1848 } else {
1849 AtomicString computedLocale = computeInheritedLanguage();
1850 parameters.locale = computedLocale.isEmpty() ? defaultLanguage() : computedLocale;
1853 StepRange stepRange = createStepRange(RejectAny);
1854 if (stepRange.hasStep()) {
1855 parameters.step = stepRange.step().toDouble();
1856 parameters.stepBase = stepRange.stepBase().toDouble();
1857 } else {
1858 parameters.step = 1.0;
1859 parameters.stepBase = 0;
1862 parameters.anchorRectInRootFrame = document().view()->contentsToRootFrame(pixelSnappedBoundingBox());
1863 parameters.anchorRectInScreen = document().view()->contentsToScreen(pixelSnappedBoundingBox());
1864 parameters.currentValue = value();
1865 parameters.doubleValue = m_inputType->valueAsDouble();
1866 parameters.isAnchorElementRTL = m_inputType->computedTextDirection() == RTL;
1867 if (HTMLDataListElement* dataList = this->dataList()) {
1868 RefPtrWillBeRawPtr<HTMLDataListOptionsCollection> options = dataList->options();
1869 for (unsigned i = 0; HTMLOptionElement* option = options->item(i); ++i) {
1870 if (!isValidValue(option->value()))
1871 continue;
1872 DateTimeSuggestion suggestion;
1873 suggestion.value = m_inputType->parseToNumber(option->value(), Decimal::nan()).toDouble();
1874 if (std::isnan(suggestion.value))
1875 continue;
1876 suggestion.localizedValue = localizeValue(option->value());
1877 suggestion.label = option->value() == option->label() ? String() : option->label();
1878 parameters.suggestions.append(suggestion);
1881 return true;
1884 bool HTMLInputElement::supportsInputModeAttribute() const
1886 return m_inputType->supportsInputModeAttribute();
1889 void HTMLInputElement::setShouldRevealPassword(bool value)
1891 if (!!m_shouldRevealPassword == value)
1892 return;
1893 m_shouldRevealPassword = value;
1894 lazyReattachIfAttached();
1897 bool HTMLInputElement::isInteractiveContent() const
1899 return m_inputType->isInteractiveContent();
1902 bool HTMLInputElement::supportsAutofocus() const
1904 return m_inputType->isInteractiveContent();
1907 PassRefPtr<ComputedStyle> HTMLInputElement::customStyleForLayoutObject()
1909 return m_inputTypeView->customStyleForLayoutObject(originalStyleForLayoutObject());
1912 bool HTMLInputElement::shouldDispatchFormControlChangeEvent(String& oldValue, String& newValue)
1914 return m_inputType->shouldDispatchFormControlChangeEvent(oldValue, newValue);
1917 void HTMLInputElement::didNotifySubtreeInsertionsToDocument()
1919 listAttributeTargetChanged();
1922 AXObject* HTMLInputElement::popupRootAXObject()
1924 return m_inputTypeView->popupRootAXObject();
1927 void HTMLInputElement::ensureFallbackContent()
1929 m_inputTypeView->ensureFallbackContent();
1932 void HTMLInputElement::ensurePrimaryContent()
1934 m_inputTypeView->ensurePrimaryContent();
1937 bool HTMLInputElement::hasFallbackContent() const
1939 return m_inputTypeView->hasFallbackContent();
1941 } // namespace