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 Apple Inc. All rights reserved.
6 * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7 * Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
27 #include "HTMLInputElement.h"
29 #include "ChromeClient.h"
30 #include "CSSPropertyNames.h"
34 #include "EventHandler.h"
35 #include "EventNames.h"
38 #include "FocusController.h"
39 #include "FormDataList.h"
41 #include "HTMLFormElement.h"
42 #include "HTMLImageLoader.h"
43 #include "HTMLNames.h"
44 #include "KeyboardEvent.h"
45 #include "LocalizedStrings.h"
46 #include "MouseEvent.h"
48 #include "RenderButton.h"
49 #include "RenderFileUploadControl.h"
50 #include "RenderImage.h"
51 #include "RenderSlider.h"
52 #include "RenderText.h"
53 #include "RenderTextControlSingleLine.h"
54 #include "RenderTheme.h"
55 #include "TextEvent.h"
56 #include <wtf/StdLibExtras.h>
62 using namespace HTMLNames;
64 const int maxSavedResults = 256;
66 HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f)
67 : HTMLFormControlElementWithState(tagName, doc, f)
74 , m_defaultChecked(false)
75 , m_useDefaultChecked(true)
76 , m_indeterminate(false)
78 , m_activeSubmit(false)
79 , m_autocomplete(Uninitialized)
83 ASSERT(hasTagName(inputTag) || hasTagName(isindexTag));
86 HTMLInputElement::~HTMLInputElement()
88 if (needsActivationCallback())
89 document()->unregisterForDocumentActivationCallbacks(this);
91 document()->checkedRadioButtons().removeButton(this);
93 // Need to remove this from the form while it is still an HTMLInputElement,
94 // so can't wait for the base class's destructor to do it.
98 const AtomicString& HTMLInputElement::name() const
100 return m_data.name();
103 bool HTMLInputElement::autoComplete() const
105 if (m_autocomplete != Uninitialized)
106 return m_autocomplete == On;
108 // Assuming we're still in a Form, respect the Form's setting
109 if (HTMLFormElement* form = this->form())
110 return form->autoComplete();
112 // The default is true
117 static inline HTMLFormElement::CheckedRadioButtons& checkedRadioButtons(const HTMLInputElement *element)
119 if (HTMLFormElement* form = element->form())
120 return form->checkedRadioButtons();
122 return element->document()->checkedRadioButtons();
125 bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const
127 // If text fields can be focused, then they should always be keyboard focusable
129 return HTMLFormControlElementWithState::isFocusable();
131 // If the base class says we can't be focused, then we can stop now.
132 if (!HTMLFormControlElementWithState::isKeyboardFocusable(event))
135 if (inputType() == RADIO) {
137 // Never allow keyboard tabbing to leave you in the same radio group. Always
138 // skip any other elements in the group.
139 Node* currentFocusedNode = document()->focusedNode();
140 if (currentFocusedNode && currentFocusedNode->hasTagName(inputTag)) {
141 HTMLInputElement* focusedInput = static_cast<HTMLInputElement*>(currentFocusedNode);
142 if (focusedInput->inputType() == RADIO && focusedInput->form() == form() &&
143 focusedInput->name() == name())
147 // Allow keyboard focus if we're checked or if nothing in the group is checked.
148 return checked() || !checkedRadioButtons(this).checkedButtonForGroup(name());
154 bool HTMLInputElement::isMouseFocusable() const
157 return HTMLFormControlElementWithState::isFocusable();
158 return HTMLFormControlElementWithState::isMouseFocusable();
161 void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
164 InputElement::updateFocusAppearance(m_data, document(), restorePreviousSelection);
166 HTMLFormControlElementWithState::updateFocusAppearance(restorePreviousSelection);
169 void HTMLInputElement::aboutToUnload()
171 InputElement::aboutToUnload(m_data, document());
174 bool HTMLInputElement::shouldUseInputMethod() const
176 return m_type == TEXT || m_type == SEARCH || m_type == ISINDEX;
179 void HTMLInputElement::dispatchFocusEvent()
181 InputElement::dispatchFocusEvent(m_data, document());
184 m_autofilled = false;
186 HTMLFormControlElementWithState::dispatchFocusEvent();
189 void HTMLInputElement::dispatchBlurEvent()
191 InputElement::dispatchBlurEvent(m_data, document());
192 HTMLFormControlElementWithState::dispatchBlurEvent();
195 void HTMLInputElement::setType(const String& t)
199 removeAttribute(typeAttr, exccode);
201 setAttribute(typeAttr, t);
204 void HTMLInputElement::setInputType(const String& t)
208 if (equalIgnoringCase(t, "password"))
210 else if (equalIgnoringCase(t, "checkbox"))
212 else if (equalIgnoringCase(t, "radio"))
214 else if (equalIgnoringCase(t, "submit"))
216 else if (equalIgnoringCase(t, "reset"))
218 else if (equalIgnoringCase(t, "file"))
220 else if (equalIgnoringCase(t, "hidden"))
222 else if (equalIgnoringCase(t, "image"))
224 else if (equalIgnoringCase(t, "button"))
226 else if (equalIgnoringCase(t, "khtml_isindex"))
228 else if (equalIgnoringCase(t, "search"))
230 else if (equalIgnoringCase(t, "range"))
235 // IMPORTANT: Don't allow the type to be changed to FILE after the first
236 // type change, otherwise a JavaScript programmer would be able to set a text
237 // field's value to something like /etc/passwd and then change it to a file field.
238 if (inputType() != newType) {
239 if (newType == FILE && m_haveType)
240 // Set the attribute back to the old value.
241 // Useful in case we were called from inside parseMappedAttribute.
242 setAttribute(typeAttr, type());
244 checkedRadioButtons(this).removeButton(this);
246 if (newType == FILE && !m_fileList)
247 m_fileList = FileList::create();
249 bool wasAttached = attached();
253 bool didStoreValue = storesValueSeparateFromAttribute();
254 bool wasPasswordField = inputType() == PASSWORD;
255 bool didRespectHeightAndWidth = respectHeightAndWidthAttrs();
257 bool willStoreValue = storesValueSeparateFromAttribute();
258 bool isPasswordField = inputType() == PASSWORD;
259 bool willRespectHeightAndWidth = respectHeightAndWidthAttrs();
261 if (didStoreValue && !willStoreValue && !m_data.value().isNull()) {
262 setAttribute(valueAttr, m_data.value());
263 m_data.setValue(String());
265 if (!didStoreValue && willStoreValue)
266 m_data.setValue(constrainValue(getAttribute(valueAttr)));
268 InputElement::updateValueIfNeeded(m_data);
270 if (wasPasswordField && !isPasswordField)
271 unregisterForActivationCallbackIfNeeded();
272 else if (!wasPasswordField && isPasswordField)
273 registerForActivationCallbackIfNeeded();
275 if (didRespectHeightAndWidth != willRespectHeightAndWidth) {
276 NamedMappedAttrMap* map = mappedAttributes();
277 if (Attribute* height = map->getAttributeItem(heightAttr))
278 attributeChanged(height, false);
279 if (Attribute* width = map->getAttributeItem(widthAttr))
280 attributeChanged(width, false);
281 if (Attribute* align = map->getAttributeItem(alignAttr))
282 attributeChanged(align, false);
287 if (document()->focusedNode() == this)
288 updateFocusAppearance(true);
291 checkedRadioButtons(this).addButton(this);
294 InputElement::notifyFormStateChanged(m_data, document());
298 if (inputType() != IMAGE && m_imageLoader)
299 m_imageLoader.clear();
302 const AtomicString& HTMLInputElement::type() const
304 // needs to be lowercase according to DOM spec
305 switch (inputType()) {
307 DEFINE_STATIC_LOCAL(const AtomicString, button, ("button"));
311 DEFINE_STATIC_LOCAL(const AtomicString, checkbox, ("checkbox"));
315 DEFINE_STATIC_LOCAL(const AtomicString, file, ("file"));
319 DEFINE_STATIC_LOCAL(const AtomicString, hidden, ("hidden"));
323 DEFINE_STATIC_LOCAL(const AtomicString, image, ("image"));
329 DEFINE_STATIC_LOCAL(const AtomicString, password, ("password"));
333 DEFINE_STATIC_LOCAL(const AtomicString, radio, ("radio"));
337 DEFINE_STATIC_LOCAL(const AtomicString, range, ("range"));
341 DEFINE_STATIC_LOCAL(const AtomicString, reset, ("reset"));
345 DEFINE_STATIC_LOCAL(const AtomicString, search, ("search"));
349 DEFINE_STATIC_LOCAL(const AtomicString, submit, ("submit"));
353 DEFINE_STATIC_LOCAL(const AtomicString, text, ("text"));
360 bool HTMLInputElement::saveState(String& result) const
365 switch (inputType()) {
380 result = checked() ? "on" : "off";
385 ASSERT_NOT_REACHED();
389 void HTMLInputElement::restoreState(const String& state)
391 ASSERT(inputType() != PASSWORD); // should never save/restore password fields
392 switch (inputType()) {
407 setChecked(state == "on");
414 bool HTMLInputElement::canStartSelection() const
418 return HTMLFormControlElementWithState::canStartSelection();
421 bool HTMLInputElement::canHaveSelection() const
423 return isTextField();
426 int HTMLInputElement::selectionStart() const
430 if (document()->focusedNode() != this && m_data.cachedSelectionStart() != -1)
431 return m_data.cachedSelectionStart();
434 return toRenderTextControl(renderer())->selectionStart();
437 int HTMLInputElement::selectionEnd() const
441 if (document()->focusedNode() != this && m_data.cachedSelectionEnd() != -1)
442 return m_data.cachedSelectionEnd();
445 return toRenderTextControl(renderer())->selectionEnd();
448 void HTMLInputElement::setSelectionStart(int start)
454 toRenderTextControl(renderer())->setSelectionStart(start);
457 void HTMLInputElement::setSelectionEnd(int end)
463 toRenderTextControl(renderer())->setSelectionEnd(end);
466 void HTMLInputElement::select()
472 toRenderTextControl(renderer())->select();
475 void HTMLInputElement::setSelectionRange(int start, int end)
477 InputElement::updateSelectionRange(m_data, start, end);
480 void HTMLInputElement::accessKeyAction(bool sendToAnyElement)
482 switch (inputType()) {
492 // send the mouse button events iff the caller specified sendToAnyElement
493 dispatchSimulatedClick(0, sendToAnyElement);
496 // a no-op for this type
502 // should never restore previous selection here
508 bool HTMLInputElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
510 if (((attrName == heightAttr || attrName == widthAttr) && respectHeightAndWidthAttrs()) ||
511 attrName == vspaceAttr ||
512 attrName == hspaceAttr) {
517 if (attrName == alignAttr) {
518 if (inputType() == IMAGE) {
519 // Share with <img> since the alignment behavior is the same.
525 return HTMLElement::mapToEntry(attrName, result);
528 void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr)
530 if (attr->name() == nameAttr) {
531 checkedRadioButtons(this).removeButton(this);
532 m_data.setName(attr->value());
533 checkedRadioButtons(this).addButton(this);
534 } else if (attr->name() == autocompleteAttr) {
535 if (equalIgnoringCase(attr->value(), "off")) {
536 m_autocomplete = Off;
537 registerForActivationCallbackIfNeeded();
539 if (m_autocomplete == Off)
540 unregisterForActivationCallbackIfNeeded();
542 m_autocomplete = Uninitialized;
546 } else if (attr->name() == typeAttr) {
547 setInputType(attr->value());
548 } else if (attr->name() == valueAttr) {
549 // We only need to setChanged if the form is looking at the default value right now.
550 if (m_data.value().isNull())
552 setValueMatchesRenderer(false);
553 } else if (attr->name() == checkedAttr) {
554 m_defaultChecked = !attr->isNull();
555 if (m_useDefaultChecked) {
556 setChecked(m_defaultChecked);
557 m_useDefaultChecked = true;
559 } else if (attr->name() == maxlengthAttr)
560 InputElement::parseMaxLengthAttribute(m_data, attr);
561 else if (attr->name() == sizeAttr)
562 InputElement::parseSizeAttribute(m_data, attr);
563 else if (attr->name() == altAttr) {
564 if (renderer() && inputType() == IMAGE)
565 toRenderImage(renderer())->updateAltText();
566 } else if (attr->name() == srcAttr) {
567 if (renderer() && inputType() == IMAGE) {
569 m_imageLoader.set(new HTMLImageLoader(this));
570 m_imageLoader->updateFromElementIgnoringPreviousError();
572 } else if (attr->name() == usemapAttr ||
573 attr->name() == accesskeyAttr) {
574 // FIXME: ignore for the moment
575 } else if (attr->name() == vspaceAttr) {
576 addCSSLength(attr, CSSPropertyMarginTop, attr->value());
577 addCSSLength(attr, CSSPropertyMarginBottom, attr->value());
578 } else if (attr->name() == hspaceAttr) {
579 addCSSLength(attr, CSSPropertyMarginLeft, attr->value());
580 addCSSLength(attr, CSSPropertyMarginRight, attr->value());
581 } else if (attr->name() == alignAttr) {
582 if (inputType() == IMAGE)
583 addHTMLAlignment(attr);
584 } else if (attr->name() == widthAttr) {
585 if (respectHeightAndWidthAttrs())
586 addCSSLength(attr, CSSPropertyWidth, attr->value());
587 } else if (attr->name() == heightAttr) {
588 if (respectHeightAndWidthAttrs())
589 addCSSLength(attr, CSSPropertyHeight, attr->value());
590 } else if (attr->name() == onfocusAttr) {
591 setInlineEventListenerForTypeAndAttribute(eventNames().focusEvent, attr);
592 } else if (attr->name() == onblurAttr) {
593 setInlineEventListenerForTypeAndAttribute(eventNames().blurEvent, attr);
594 } else if (attr->name() == onselectAttr) {
595 setInlineEventListenerForTypeAndAttribute(eventNames().selectEvent, attr);
596 } else if (attr->name() == onchangeAttr) {
597 setInlineEventListenerForTypeAndAttribute(eventNames().changeEvent, attr);
598 } else if (attr->name() == oninputAttr) {
599 setInlineEventListenerForTypeAndAttribute(eventNames().inputEvent, attr);
601 // Search field and slider attributes all just cause updateFromElement to be called through style
603 else if (attr->name() == onsearchAttr) {
604 setInlineEventListenerForTypeAndAttribute(eventNames().searchEvent, attr);
605 } else if (attr->name() == resultsAttr) {
606 int oldResults = m_maxResults;
607 m_maxResults = !attr->isNull() ? min(attr->value().toInt(), maxSavedResults) : -1;
608 // FIXME: Detaching just for maxResults change is not ideal. We should figure out the right
609 // time to relayout for this change.
610 if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0) && attached()) {
615 } else if (attr->name() == placeholderAttr) {
617 InputElement::updatePlaceholderVisibility(m_data, document(), true);
618 } else if (attr->name() == autosaveAttr ||
619 attr->name() == incrementalAttr ||
620 attr->name() == minAttr ||
621 attr->name() == maxAttr ||
622 attr->name() == multipleAttr ||
623 attr->name() == precisionAttr)
626 HTMLFormControlElementWithState::parseMappedAttribute(attr);
629 bool HTMLInputElement::rendererIsNeeded(RenderStyle *style)
631 switch (inputType()) {
644 return HTMLFormControlElementWithState::rendererIsNeeded(style);
652 RenderObject *HTMLInputElement::createRenderer(RenderArena *arena, RenderStyle *style)
654 switch (inputType()) {
658 return new (arena) RenderButton(this);
661 return RenderObject::createObject(this, style);
663 return new (arena) RenderFileUploadControl(this);
667 return new (arena) RenderImage(this);
669 return new (arena) RenderSlider(this);
674 return new (arena) RenderTextControlSingleLine(this);
680 void HTMLInputElement::attach()
684 setInputType(getAttribute(typeAttr));
688 HTMLFormControlElementWithState::attach();
690 if (inputType() == IMAGE) {
692 m_imageLoader.set(new HTMLImageLoader(this));
693 m_imageLoader->updateFromElement();
695 RenderImage* imageObj = toRenderImage(renderer());
696 imageObj->setCachedImage(m_imageLoader->image());
698 // If we have no image at all because we have no src attribute, set
699 // image height and width for the alt text instead.
700 if (!m_imageLoader->image() && !imageObj->cachedImage())
701 imageObj->setImageSizeForAltText();
706 void HTMLInputElement::detach()
708 HTMLFormControlElementWithState::detach();
709 setValueMatchesRenderer(false);
712 String HTMLInputElement::altText() const
714 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
715 // also heavily discussed by Hixie on bugzilla
716 // note this is intentionally different to HTMLImageElement::altText()
717 String alt = getAttribute(altAttr);
718 // fall back to title attribute
720 alt = getAttribute(titleAttr);
722 alt = getAttribute(valueAttr);
724 alt = inputElementAltText();
728 bool HTMLInputElement::isSuccessfulSubmitButton() const
730 // HTML spec says that buttons must have names to be considered successful.
731 // However, other browsers do not impose this constraint. So we do likewise.
732 return !disabled() && (inputType() == IMAGE || inputType() == SUBMIT);
735 bool HTMLInputElement::isActivatedSubmit() const
737 return m_activeSubmit;
740 void HTMLInputElement::setActivatedSubmit(bool flag)
742 m_activeSubmit = flag;
745 bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart)
747 // image generates its own names, but for other types there is no form data unless there's a name
748 if (name().isEmpty() && inputType() != IMAGE)
751 switch (inputType()) {
759 encoding.appendData(name(), value());
765 encoding.appendData(name(), value());
772 // these types of buttons are never successful
776 if (m_activeSubmit) {
777 encoding.appendData(name().isEmpty() ? "x" : (name() + ".x"), m_xPos);
778 encoding.appendData(name().isEmpty() ? "y" : (name() + ".y"), m_yPos);
779 if (!name().isEmpty() && !value().isEmpty())
780 encoding.appendData(name(), value());
786 if (m_activeSubmit) {
787 String enc_str = valueWithDefault();
788 encoding.appendData(name(), enc_str);
794 // Can't submit file on GET.
798 // If no filename at all is entered, return successful but empty.
799 // Null would be more logical, but Netscape posts an empty file. Argh.
800 unsigned numFiles = m_fileList->length();
802 encoding.appendFile(name(), File::create(""));
806 for (unsigned i = 0; i < numFiles; ++i)
807 encoding.appendFile(name(), m_fileList->item(i));
814 void HTMLInputElement::reset()
816 if (storesValueSeparateFromAttribute())
819 setChecked(m_defaultChecked);
820 m_useDefaultChecked = true;
823 void HTMLInputElement::setChecked(bool nowChecked, bool sendChangeEvent)
825 if (checked() == nowChecked)
828 checkedRadioButtons(this).removeButton(this);
830 m_useDefaultChecked = false;
831 m_checked = nowChecked;
834 checkedRadioButtons(this).addButton(this);
836 if (renderer() && renderer()->style()->hasAppearance())
837 theme()->stateChanged(renderer(), CheckedState);
839 // Only send a change event for items in the document (avoid firing during
840 // parsing) and don't send a change event for a radio button that's getting
841 // unchecked to match other browsers. DOM is not a useful standard for this
842 // because it says only to fire change events at "lose focus" time, which is
843 // definitely wrong in practice for these types of elements.
844 if (sendChangeEvent && inDocument() && (inputType() != RADIO || nowChecked))
848 void HTMLInputElement::setIndeterminate(bool _indeterminate)
850 // Only checkboxes honor indeterminate.
851 if (inputType() != CHECKBOX || indeterminate() == _indeterminate)
854 m_indeterminate = _indeterminate;
858 if (renderer() && renderer()->style()->hasAppearance())
859 theme()->stateChanged(renderer(), CheckedState);
862 int HTMLInputElement::size() const
864 return m_data.size();
867 void HTMLInputElement::copyNonAttributeProperties(const Element* source)
869 const HTMLInputElement* sourceElement = static_cast<const HTMLInputElement*>(source);
871 m_data.setValue(sourceElement->m_data.value());
872 m_checked = sourceElement->m_checked;
873 m_indeterminate = sourceElement->m_indeterminate;
875 HTMLFormControlElementWithState::copyNonAttributeProperties(source);
878 String HTMLInputElement::value() const
880 // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't applicable to the file upload control
881 // but we don't want to break existing websites, who may be relying on being able to get the file name as a value.
882 if (inputType() == FILE) {
883 if (!m_fileList->isEmpty())
884 return m_fileList->item(0)->fileName();
888 String value = m_data.value();
889 if (value.isNull()) {
890 value = constrainValue(getAttribute(valueAttr));
892 // If no attribute exists, then just use "on" or "" based off the checked() state of the control.
893 if (value.isNull() && (inputType() == CHECKBOX || inputType() == RADIO))
894 return checked() ? "on" : "";
900 String HTMLInputElement::valueWithDefault() const
904 switch (inputType()) {
918 v = resetButtonDefaultLabel();
921 v = submitButtonDefaultLabel();
928 void HTMLInputElement::setValue(const String& value)
930 // For security reasons, we don't allow setting the filename, but we do allow clearing it.
931 // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't applicable to the file upload control
932 // but we don't want to break existing websites, who may be relying on this method to clear things.
933 if (inputType() == FILE && !value.isEmpty())
936 setValueMatchesRenderer(false);
937 if (storesValueSeparateFromAttribute()) {
938 if (inputType() == FILE)
941 m_data.setValue(constrainValue(value));
943 InputElement::updatePlaceholderVisibility(m_data, document());
945 document()->updateRendering();
949 renderer()->updateFromElement();
952 setAttribute(valueAttr, constrainValue(value));
955 unsigned max = m_data.value().length();
956 if (document()->focusedNode() == this)
957 InputElement::updateSelectionRange(m_data, max, max);
959 cacheSelection(max, max);
961 InputElement::notifyFormStateChanged(m_data, document());
964 String HTMLInputElement::placeholderValue() const
966 return getAttribute(placeholderAttr).string();
969 bool HTMLInputElement::searchEventsShouldBeDispatched() const
971 return hasAttribute(incrementalAttr);
974 void HTMLInputElement::setValueFromRenderer(const String& value)
976 // File upload controls will always use setFileListFromRenderer.
977 ASSERT(inputType() != FILE);
978 InputElement::setValueFromRenderer(m_data, document(), value);
981 void HTMLInputElement::setFileListFromRenderer(const Vector<String>& paths)
984 int size = paths.size();
985 for (int i = 0; i < size; i++)
986 m_fileList->append(File::create(paths[i]));
988 setValueMatchesRenderer();
989 InputElement::notifyFormStateChanged(m_data, document());
992 bool HTMLInputElement::storesValueSeparateFromAttribute() const
994 switch (inputType()) {
1014 void* HTMLInputElement::preDispatchEventHandler(Event *evt)
1016 // preventDefault or "return false" are used to reverse the automatic checking/selection we do here.
1017 // This result gives us enough info to perform the "undo" in postDispatch of the action we take here.
1019 if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
1020 && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1021 if (inputType() == CHECKBOX) {
1022 // As a way to store the state, we return 0 if we were unchecked, 1 if we were checked, and 2 for
1024 if (indeterminate()) {
1025 result = (void*)0x2;
1026 setIndeterminate(false);
1029 result = (void*)0x1;
1030 setChecked(!checked(), true);
1033 // For radio buttons, store the current selected radio object.
1034 // We really want radio groups to end up in sane states, i.e., to have something checked.
1035 // Therefore if nothing is currently selected, we won't allow this action to be "undone", since
1036 // we want some object in the radio group to actually get selected.
1037 HTMLInputElement* currRadio = checkedRadioButtons(this).checkedButtonForGroup(name());
1039 // We have a radio button selected that is not us. Cache it in our result field and ref it so
1040 // that it can't be destroyed.
1044 setChecked(true, true);
1050 void HTMLInputElement::postDispatchEventHandler(Event *evt, void* data)
1052 if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
1053 && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1054 if (inputType() == CHECKBOX) {
1055 // Reverse the checking we did in preDispatch.
1056 if (evt->defaultPrevented() || evt->defaultHandled()) {
1057 if (data == (void*)0x2)
1058 setIndeterminate(true);
1063 HTMLInputElement* input = static_cast<HTMLInputElement*>(data);
1064 if (evt->defaultPrevented() || evt->defaultHandled()) {
1065 // Restore the original selected radio button if possible.
1066 // Make sure it is still a radio button and only do the restoration if it still
1067 // belongs to our group.
1069 if (input->form() == form() && input->inputType() == RADIO && input->name() == name()) {
1070 // Ok, the old radio button is still in our form and in our group and is still a
1071 // radio button, so it's safe to restore selection to it.
1072 input->setChecked(true);
1078 // Left clicks on radio buttons and check boxes already performed default actions in preDispatchEventHandler().
1079 evt->setDefaultHandled();
1083 void HTMLInputElement::defaultEventHandler(Event* evt)
1085 // FIXME: It would be better to refactor this for the different types of input element.
1086 // Having them all in one giant function makes this hard to read, and almost all the handling is type-specific.
1088 bool clickDefaultFormButton = false;
1090 if (isTextField() && evt->type() == eventNames().textInputEvent && evt->isTextEvent() && static_cast<TextEvent*>(evt)->data() == "\n")
1091 clickDefaultFormButton = true;
1093 if (inputType() == IMAGE && evt->isMouseEvent() && evt->type() == eventNames().clickEvent) {
1094 // record the mouse position for when we get the DOMActivate event
1095 MouseEvent* me = static_cast<MouseEvent*>(evt);
1096 // FIXME: We could just call offsetX() and offsetY() on the event,
1097 // but that's currently broken, so for now do the computation here.
1098 if (me->isSimulated() || !renderer()) {
1102 // FIXME: This doesn't work correctly with transforms.
1103 IntPoint absOffset = roundedIntPoint(renderer()->localToAbsolute());
1104 m_xPos = me->pageX() - absOffset.x();
1105 m_yPos = me->pageY() - absOffset.y();
1110 && evt->type() == eventNames().keydownEvent
1111 && evt->isKeyboardEvent()
1113 && document()->frame()
1114 && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) {
1115 evt->setDefaultHandled();
1119 if (inputType() == RADIO
1120 && evt->isMouseEvent()
1121 && evt->type() == eventNames().clickEvent
1122 && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1123 evt->setDefaultHandled();
1127 // Call the base event handler before any of our own event handling for almost all events in text fields.
1128 // Makes editing keyboard handling take precedence over the keydown and keypress handling in this function.
1129 bool callBaseClassEarly = isTextField() && !clickDefaultFormButton
1130 && (evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent);
1131 if (callBaseClassEarly) {
1132 HTMLFormControlElementWithState::defaultEventHandler(evt);
1133 if (evt->defaultHandled())
1137 // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means
1138 // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks
1139 // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element
1140 // must dispatch a DOMActivate event - a click event will not do the job.
1141 if (evt->type() == eventNames().DOMActivateEvent && !disabled()) {
1142 if (inputType() == IMAGE || inputType() == SUBMIT || inputType() == RESET) {
1145 if (inputType() == RESET)
1148 m_activeSubmit = true;
1149 // FIXME: Would be cleaner to get m_xPos and m_yPos out of the underlying mouse
1150 // event (if any) here instead of relying on the variables set above when
1151 // processing the click event. Even better, appendFormData could pass the
1152 // event in, and then we could get rid of m_xPos and m_yPos altogether!
1153 if (!form()->prepareSubmit(evt)) {
1157 m_activeSubmit = false;
1159 } else if (inputType() == FILE && renderer())
1160 static_cast<RenderFileUploadControl*>(renderer())->click();
1163 // Use key press event here since sending simulated mouse events
1164 // on key down blocks the proper sending of the key press event.
1165 if (evt->type() == eventNames().keypressEvent && evt->isKeyboardEvent()) {
1166 bool clickElement = false;
1168 int charCode = static_cast<KeyboardEvent*>(evt)->charCode();
1170 if (charCode == '\r') {
1171 switch (inputType()) {
1179 // Simulate mouse click on the default form button for enter for these types of elements.
1180 clickDefaultFormButton = true;
1187 // Simulate mouse click for enter for these types of elements.
1188 clickElement = true;
1191 break; // Don't do anything for enter on a radio button.
1193 } else if (charCode == ' ') {
1194 switch (inputType()) {
1202 // Prevent scrolling down the page.
1203 evt->setDefaultHandled();
1211 dispatchSimulatedClick(evt);
1212 evt->setDefaultHandled();
1217 if (evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent()) {
1218 String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
1220 if (key == "U+0020") {
1221 switch (inputType()) {
1229 setActive(true, true);
1230 // No setDefaultHandled(), because IE dispatches a keypress in this case
1231 // and the caller will only dispatch a keypress if we don't call setDefaultHandled.
1238 if (inputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) {
1239 // Left and up mean "previous radio button".
1240 // Right and down mean "next radio button".
1241 // Tested in WinIE, and even for RTL, left still means previous radio button (and so moves
1242 // to the right). Seems strange, but we'll match it.
1243 bool forward = (key == "Down" || key == "Right");
1245 // We can only stay within the form's children if the form hasn't been demoted to a leaf because
1246 // of malformed HTML.
1248 while ((n = (forward ? n->traverseNextNode() : n->traversePreviousNode()))) {
1249 // Once we encounter a form element, we know we're through.
1250 if (n->hasTagName(formTag))
1253 // Look for more radio buttons.
1254 if (n->hasTagName(inputTag)) {
1255 HTMLInputElement* elt = static_cast<HTMLInputElement*>(n);
1256 if (elt->form() != form())
1258 if (n->hasTagName(inputTag)) {
1259 HTMLInputElement* inputElt = static_cast<HTMLInputElement*>(n);
1260 if (inputElt->inputType() == RADIO && inputElt->name() == name() && inputElt->isFocusable()) {
1261 inputElt->setChecked(true);
1262 document()->setFocusedNode(inputElt);
1263 inputElt->dispatchSimulatedClick(evt, false, false);
1264 evt->setDefaultHandled();
1273 if (evt->type() == eventNames().keyupEvent && evt->isKeyboardEvent()) {
1274 bool clickElement = false;
1276 String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
1278 if (key == "U+0020") {
1279 switch (inputType()) {
1286 // Simulate mouse click for spacebar for these types of elements.
1287 // The AppKit already does this for some, but not all, of them.
1288 clickElement = true;
1291 // If an unselected radio is tabbed into (because the entire group has nothing
1292 // checked, or because of some explicit .focus() call), then allow space to check it.
1294 clickElement = true;
1308 dispatchSimulatedClick(evt);
1309 evt->setDefaultHandled();
1314 if (clickDefaultFormButton) {
1315 if (isSearchField()) {
1319 // Fire onChange for text fields.
1320 RenderObject* r = renderer();
1321 if (r && r->isTextField() && toRenderTextControl(r)->isEdited()) {
1323 // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it.
1325 if (r && r->isTextField())
1326 toRenderTextControl(r)->setEdited(false);
1328 // Form may never have been present, or may have been destroyed by the change event.
1330 form()->submitClick(evt);
1331 evt->setDefaultHandled();
1335 if (evt->isBeforeTextInsertedEvent())
1336 InputElement::handleBeforeTextInsertedEvent(m_data, document(), evt);
1338 if (isTextField() && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == eventNames().blurEvent || evt->type() == eventNames().focusEvent))
1339 static_cast<RenderTextControlSingleLine*>(renderer())->forwardEvent(evt);
1341 if (inputType() == RANGE && renderer()) {
1342 RenderSlider* slider = static_cast<RenderSlider*>(renderer());
1343 if (evt->isMouseEvent() && evt->type() == eventNames().mousedownEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1344 MouseEvent* mEvt = static_cast<MouseEvent*>(evt);
1345 if (!slider->mouseEventIsInThumb(mEvt)) {
1346 IntPoint eventOffset(mEvt->offsetX(), mEvt->offsetY());
1347 if (mEvt->target() != this) // Does this ever happen now? Was originally added for <video> controls.
1348 eventOffset = roundedIntPoint(renderer()->absoluteToLocal(FloatPoint(mEvt->pageX(), mEvt->pageY()), false, true));
1349 slider->setValueForPosition(slider->positionForOffset(eventOffset));
1352 if (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent())
1353 slider->forwardEvent(evt);
1356 if (!callBaseClassEarly && !evt->defaultHandled())
1357 HTMLFormControlElementWithState::defaultEventHandler(evt);
1360 bool HTMLInputElement::isURLAttribute(Attribute *attr) const
1362 return (attr->name() == srcAttr);
1365 String HTMLInputElement::defaultValue() const
1367 return getAttribute(valueAttr);
1370 void HTMLInputElement::setDefaultValue(const String &value)
1372 setAttribute(valueAttr, value);
1375 bool HTMLInputElement::defaultChecked() const
1377 return !getAttribute(checkedAttr).isNull();
1380 void HTMLInputElement::setDefaultChecked(bool defaultChecked)
1382 setAttribute(checkedAttr, defaultChecked ? "" : 0);
1385 void HTMLInputElement::setDefaultName(const AtomicString& name)
1387 m_data.setName(name);
1390 String HTMLInputElement::accept() const
1392 return getAttribute(acceptAttr);
1395 void HTMLInputElement::setAccept(const String &value)
1397 setAttribute(acceptAttr, value);
1400 String HTMLInputElement::accessKey() const
1402 return getAttribute(accesskeyAttr);
1405 void HTMLInputElement::setAccessKey(const String &value)
1407 setAttribute(accesskeyAttr, value);
1410 String HTMLInputElement::align() const
1412 return getAttribute(alignAttr);
1415 void HTMLInputElement::setAlign(const String &value)
1417 setAttribute(alignAttr, value);
1420 String HTMLInputElement::alt() const
1422 return getAttribute(altAttr);
1425 void HTMLInputElement::setAlt(const String &value)
1427 setAttribute(altAttr, value);
1430 int HTMLInputElement::maxLength() const
1432 return m_data.maxLength();
1435 void HTMLInputElement::setMaxLength(int _maxLength)
1437 setAttribute(maxlengthAttr, String::number(_maxLength));
1440 bool HTMLInputElement::multiple() const
1442 return !getAttribute(multipleAttr).isNull();
1445 void HTMLInputElement::setMultiple(bool multiple)
1447 setAttribute(multipleAttr, multiple ? "" : 0);
1450 void HTMLInputElement::setSize(unsigned _size)
1452 setAttribute(sizeAttr, String::number(_size));
1455 KURL HTMLInputElement::src() const
1457 return document()->completeURL(getAttribute(srcAttr));
1460 void HTMLInputElement::setSrc(const String &value)
1462 setAttribute(srcAttr, value);
1465 String HTMLInputElement::useMap() const
1467 return getAttribute(usemapAttr);
1470 void HTMLInputElement::setUseMap(const String &value)
1472 setAttribute(usemapAttr, value);
1475 void HTMLInputElement::setAutofilled(bool b)
1477 if (b == m_autofilled)
1484 FileList* HTMLInputElement::files()
1486 if (inputType() != FILE)
1488 return m_fileList.get();
1491 String HTMLInputElement::constrainValue(const String& proposedValue) const
1493 return InputElement::constrainValue(m_data, proposedValue, m_data.maxLength());
1496 bool HTMLInputElement::needsActivationCallback()
1498 return inputType() == PASSWORD || m_autocomplete == Off;
1501 void HTMLInputElement::registerForActivationCallbackIfNeeded()
1503 if (needsActivationCallback())
1504 document()->registerForDocumentActivationCallbacks(this);
1507 void HTMLInputElement::unregisterForActivationCallbackIfNeeded()
1509 if (!needsActivationCallback())
1510 document()->unregisterForDocumentActivationCallbacks(this);
1513 void HTMLInputElement::cacheSelection(int start, int end)
1515 m_data.setCachedSelectionStart(start);
1516 m_data.setCachedSelectionEnd(end);
1519 void HTMLInputElement::addSearchResult()
1521 ASSERT(isSearchField());
1523 static_cast<RenderTextControlSingleLine*>(renderer())->addSearchResult();
1526 void HTMLInputElement::onSearch()
1528 ASSERT(isSearchField());
1530 static_cast<RenderTextControlSingleLine*>(renderer())->stopSearchEventTimer();
1531 dispatchEventForType(eventNames().searchEvent, true, false);
1534 VisibleSelection HTMLInputElement::selection() const
1536 if (!renderer() || !isTextField() || m_data.cachedSelectionStart() == -1 || m_data.cachedSelectionEnd() == -1)
1537 return VisibleSelection();
1538 return toRenderTextControl(renderer())->selection(m_data.cachedSelectionStart(), m_data.cachedSelectionEnd());
1541 void HTMLInputElement::documentDidBecomeActive()
1543 ASSERT(needsActivationCallback());
1547 void HTMLInputElement::willMoveToNewOwnerDocument()
1549 // Always unregister for cache callbacks when leaving a document, even if we would otherwise like to be registered
1550 if (needsActivationCallback())
1551 document()->unregisterForDocumentActivationCallbacks(this);
1553 document()->checkedRadioButtons().removeButton(this);
1555 HTMLFormControlElementWithState::willMoveToNewOwnerDocument();
1558 void HTMLInputElement::didMoveToNewOwnerDocument()
1560 registerForActivationCallbackIfNeeded();
1562 HTMLFormControlElementWithState::didMoveToNewOwnerDocument();
1565 void HTMLInputElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
1567 HTMLFormControlElementWithState::addSubresourceAttributeURLs(urls);
1569 addSubresourceURL(urls, src());
1572 bool HTMLInputElement::willValidate() const
1574 // FIXME: This shall check for new WF2 input types too
1575 return HTMLFormControlElementWithState::willValidate() && inputType() != HIDDEN &&
1576 inputType() != BUTTON && inputType() != RESET;
1579 bool HTMLInputElement::placeholderShouldBeVisible() const
1581 return m_data.placeholderShouldBeVisible();