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 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 "BeforeTextInsertedEvent.h"
30 #include "CSSPropertyNames.h"
34 #include "EventHandler.h"
35 #include "EventNames.h"
36 #include "FocusController.h"
37 #include "FormDataList.h"
39 #include "HTMLFormElement.h"
40 #include "HTMLImageLoader.h"
41 #include "HTMLNames.h"
42 #include "KeyboardEvent.h"
43 #include "LocalizedStrings.h"
44 #include "MouseEvent.h"
46 #include "RenderButton.h"
47 #include "RenderFileUploadControl.h"
48 #include "RenderImage.h"
49 #include "RenderText.h"
50 #include "RenderTextControl.h"
51 #include "RenderTheme.h"
52 #include "RenderSlider.h"
53 #include "SelectionController.h"
54 #include "TextBreakIterator.h"
55 #include "TextEvent.h"
61 using namespace EventNames;
62 using namespace HTMLNames;
64 const int maxSavedResults = 256;
66 // FIXME: According to HTML4, the length attribute's value can be arbitrarily
67 // large. However, due to http://bugs.webkit.org/show_bugs.cgi?id=14536 things
68 // get rather sluggish when a text field has a larger number of characters than
69 // this, even when just clicking in the text field.
70 static const int cMaxLen = 524288;
72 static int numGraphemeClusters(const StringImpl* s)
77 TextBreakIterator* it = characterBreakIterator(s->characters(), s->length());
81 while (textBreakNext(it) != TextBreakDone)
86 static int numCharactersInGraphemeClusters(const StringImpl* s, int numGraphemeClusters)
91 TextBreakIterator* it = characterBreakIterator(s->characters(), s->length());
94 for (int i = 0; i < numGraphemeClusters; ++i)
95 if (textBreakNext(it) == TextBreakDone)
97 return textBreakCurrent(it);
100 HTMLInputElement::HTMLInputElement(Document* doc, HTMLFormElement* f)
101 : HTMLFormControlElementWithState(inputTag, doc, f)
106 HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f)
107 : HTMLFormControlElementWithState(tagName, doc, f)
112 void HTMLInputElement::init()
119 m_defaultChecked = false;
120 m_useDefaultChecked = true;
121 m_indeterminate = false;
124 m_activeSubmit = false;
125 m_autocomplete = true;
127 m_autofilled = false;
138 m_autocomplete = form()->autoComplete();
141 HTMLInputElement::~HTMLInputElement()
143 if (inputType() == PASSWORD)
144 document()->unregisterForCacheCallbacks(this);
146 document()->checkedRadioButtons().removeButton(this);
148 delete m_imageLoader;
151 const AtomicString& HTMLInputElement::name() const
153 return m_name.isNull() ? emptyAtom : m_name;
156 static inline HTMLFormElement::CheckedRadioButtons& checkedRadioButtons(const HTMLInputElement *element)
158 if (HTMLFormElement* form = element->form())
159 return form->checkedRadioButtons();
161 return element->document()->checkedRadioButtons();
164 bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const
166 // If text fields can be focused, then they should always be keyboard focusable
168 return HTMLFormControlElementWithState::isFocusable();
170 // If the base class says we can't be focused, then we can stop now.
171 if (!HTMLFormControlElementWithState::isKeyboardFocusable(event))
174 if (inputType() == RADIO) {
175 // Unnamed radio buttons are never focusable (matches WinIE).
176 if (name().isEmpty())
179 // Never allow keyboard tabbing to leave you in the same radio group. Always
180 // skip any other elements in the group.
181 Node* currentFocusedNode = document()->focusedNode();
182 if (currentFocusedNode && currentFocusedNode->hasTagName(inputTag)) {
183 HTMLInputElement* focusedInput = static_cast<HTMLInputElement*>(currentFocusedNode);
184 if (focusedInput->inputType() == RADIO && focusedInput->form() == form() &&
185 focusedInput->name() == name())
189 // Allow keyboard focus if we're checked or if nothing in the group is checked.
190 return checked() || !checkedRadioButtons(this).checkedButtonForGroup(name());
196 bool HTMLInputElement::isMouseFocusable() const
199 return HTMLFormControlElementWithState::isFocusable();
200 return HTMLFormControlElementWithState::isMouseFocusable();
203 void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
206 if (!restorePreviousSelection || cachedSelStart == -1)
209 // Restore the cached selection.
210 setSelectionRange(cachedSelStart, cachedSelEnd);
212 if (document() && document()->frame())
213 document()->frame()->revealSelection();
215 HTMLFormControlElementWithState::updateFocusAppearance(restorePreviousSelection);
218 void HTMLInputElement::aboutToUnload()
220 if (isTextField() && focused() && document()->frame())
221 document()->frame()->textFieldDidEndEditing(this);
224 bool HTMLInputElement::shouldUseInputMethod() const
226 return m_type == TEXT || m_type == SEARCH || m_type == ISINDEX;
229 void HTMLInputElement::dispatchFocusEvent()
232 setAutofilled(false);
233 if (inputType() == PASSWORD && document()->frame())
234 document()->setUseSecureKeyboardEntryWhenActive(true);
236 HTMLFormControlElementWithState::dispatchFocusEvent();
239 void HTMLInputElement::dispatchBlurEvent()
241 if (isTextField() && document()->frame()) {
242 if (inputType() == PASSWORD)
243 document()->setUseSecureKeyboardEntryWhenActive(false);
244 document()->frame()->textFieldDidEndEditing(this);
246 HTMLFormControlElementWithState::dispatchBlurEvent();
249 void HTMLInputElement::setType(const String& t)
253 removeAttribute(typeAttr, exccode);
255 setAttribute(typeAttr, t);
258 void HTMLInputElement::setInputType(const String& t)
262 if (equalIgnoringCase(t, "password"))
264 else if (equalIgnoringCase(t, "checkbox"))
266 else if (equalIgnoringCase(t, "radio"))
268 else if (equalIgnoringCase(t, "submit"))
270 else if (equalIgnoringCase(t, "reset"))
272 else if (equalIgnoringCase(t, "file"))
274 else if (equalIgnoringCase(t, "hidden"))
276 else if (equalIgnoringCase(t, "image"))
278 else if (equalIgnoringCase(t, "button"))
280 else if (equalIgnoringCase(t, "khtml_isindex"))
282 else if (equalIgnoringCase(t, "search"))
284 else if (equalIgnoringCase(t, "range"))
289 // IMPORTANT: Don't allow the type to be changed to FILE after the first
290 // type change, otherwise a JavaScript programmer would be able to set a text
291 // field's value to something like /etc/passwd and then change it to a file field.
292 if (inputType() != newType) {
293 if (newType == FILE && m_haveType)
294 // Set the attribute back to the old value.
295 // Useful in case we were called from inside parseMappedAttribute.
296 setAttribute(typeAttr, type());
298 checkedRadioButtons(this).removeButton(this);
300 bool wasAttached = m_attached;
304 bool didStoreValue = storesValueSeparateFromAttribute();
305 bool wasPasswordField = inputType() == PASSWORD;
306 bool didRespectHeightAndWidth = respectHeightAndWidthAttrs();
308 bool willStoreValue = storesValueSeparateFromAttribute();
309 bool isPasswordField = inputType() == PASSWORD;
310 bool willRespectHeightAndWidth = respectHeightAndWidthAttrs();
312 if (didStoreValue && !willStoreValue && !m_value.isNull()) {
313 setAttribute(valueAttr, m_value);
316 if (!didStoreValue && willStoreValue)
317 m_value = constrainValue(getAttribute(valueAttr));
321 if (wasPasswordField && !isPasswordField)
322 document()->unregisterForCacheCallbacks(this);
323 else if (!wasPasswordField && isPasswordField)
324 document()->registerForCacheCallbacks(this);
326 if (didRespectHeightAndWidth != willRespectHeightAndWidth) {
327 NamedMappedAttrMap* map = mappedAttributes();
328 if (MappedAttribute* height = map->getAttributeItem(heightAttr))
329 attributeChanged(height, false);
330 if (MappedAttribute* width = map->getAttributeItem(widthAttr))
331 attributeChanged(width, false);
332 if (MappedAttribute* align = map->getAttributeItem(alignAttr))
333 attributeChanged(align, false);
339 checkedRadioButtons(this).addButton(this);
344 if (inputType() != IMAGE && m_imageLoader) {
345 delete m_imageLoader;
350 const AtomicString& HTMLInputElement::type() const
352 // needs to be lowercase according to DOM spec
353 switch (inputType()) {
355 static const AtomicString button("button");
359 static const AtomicString checkbox("checkbox");
363 static const AtomicString file("file");
367 static const AtomicString hidden("hidden");
371 static const AtomicString image("image");
377 static const AtomicString password("password");
381 static const AtomicString radio("radio");
385 static const AtomicString range("range");
389 static const AtomicString reset("reset");
393 static const AtomicString search("search");
397 static const AtomicString submit("submit");
401 static const AtomicString text("text");
408 bool HTMLInputElement::saveState(String& result) const
410 switch (inputType()) {
425 result = checked() ? "on" : "off";
430 ASSERT_NOT_REACHED();
434 void HTMLInputElement::restoreState(const String& state)
436 ASSERT(inputType() != PASSWORD); // should never save/restore password fields
437 switch (inputType()) {
452 setChecked(state == "on");
459 bool HTMLInputElement::canStartSelection() const
463 return HTMLFormControlElementWithState::canStartSelection();
466 bool HTMLInputElement::canHaveSelection() const
468 return isTextField();
471 int HTMLInputElement::selectionStart() const
475 if (document()->focusedNode() != this && cachedSelStart != -1)
476 return cachedSelStart;
479 return static_cast<RenderTextControl*>(renderer())->selectionStart();
482 int HTMLInputElement::selectionEnd() const
486 if (document()->focusedNode() != this && cachedSelEnd != -1)
490 return static_cast<RenderTextControl*>(renderer())->selectionEnd();
493 void HTMLInputElement::setSelectionStart(int start)
499 static_cast<RenderTextControl*>(renderer())->setSelectionStart(start);
502 void HTMLInputElement::setSelectionEnd(int end)
508 static_cast<RenderTextControl*>(renderer())->setSelectionEnd(end);
511 void HTMLInputElement::select()
517 static_cast<RenderTextControl*>(renderer())->select();
520 void HTMLInputElement::setSelectionRange(int start, int end)
526 static_cast<RenderTextControl*>(renderer())->setSelectionRange(start, end);
529 void HTMLInputElement::accessKeyAction(bool sendToAnyElement)
531 switch (inputType()) {
541 // send the mouse button events iff the caller specified sendToAnyElement
542 dispatchSimulatedClick(0, sendToAnyElement);
545 // a no-op for this type
551 // should never restore previous selection here
557 bool HTMLInputElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
559 if (((attrName == heightAttr || attrName == widthAttr) && respectHeightAndWidthAttrs()) ||
560 attrName == vspaceAttr ||
561 attrName == hspaceAttr) {
566 if (attrName == alignAttr) {
567 if (inputType() == IMAGE) {
568 // Share with <img> since the alignment behavior is the same.
574 return HTMLElement::mapToEntry(attrName, result);
577 void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr)
579 if (attr->name() == nameAttr) {
580 checkedRadioButtons(this).removeButton(this);
581 m_name = attr->value();
582 checkedRadioButtons(this).addButton(this);
583 } else if (attr->name() == autocompleteAttr) {
584 m_autocomplete = !equalIgnoringCase(attr->value(), "off");
585 } else if (attr->name() == typeAttr) {
586 setInputType(attr->value());
587 } else if (attr->name() == valueAttr) {
588 // We only need to setChanged if the form is looking at the default value right now.
589 if (m_value.isNull())
591 setValueMatchesRenderer(false);
592 } else if (attr->name() == checkedAttr) {
593 m_defaultChecked = !attr->isNull();
594 if (m_useDefaultChecked) {
595 setChecked(m_defaultChecked);
596 m_useDefaultChecked = true;
598 } else if (attr->name() == maxlengthAttr) {
599 int oldMaxLen = m_maxLen;
600 m_maxLen = !attr->isNull() ? attr->value().toInt() : cMaxLen;
601 if (m_maxLen <= 0 || m_maxLen > cMaxLen)
603 if (oldMaxLen != m_maxLen)
606 } else if (attr->name() == sizeAttr) {
607 m_size = !attr->isNull() ? attr->value().toInt() : 20;
608 } else if (attr->name() == altAttr) {
609 if (renderer() && inputType() == IMAGE)
610 static_cast<RenderImage*>(renderer())->updateAltText();
611 } else if (attr->name() == srcAttr) {
612 if (renderer() && inputType() == IMAGE) {
614 m_imageLoader = new HTMLImageLoader(this);
615 m_imageLoader->updateFromElement();
617 } else if (attr->name() == usemapAttr ||
618 attr->name() == accesskeyAttr) {
619 // FIXME: ignore for the moment
620 } else if (attr->name() == vspaceAttr) {
621 addCSSLength(attr, CSS_PROP_MARGIN_TOP, attr->value());
622 addCSSLength(attr, CSS_PROP_MARGIN_BOTTOM, attr->value());
623 } else if (attr->name() == hspaceAttr) {
624 addCSSLength(attr, CSS_PROP_MARGIN_LEFT, attr->value());
625 addCSSLength(attr, CSS_PROP_MARGIN_RIGHT, attr->value());
626 } else if (attr->name() == alignAttr) {
627 if (inputType() == IMAGE)
628 addHTMLAlignment(attr);
629 } else if (attr->name() == widthAttr) {
630 if (respectHeightAndWidthAttrs())
631 addCSSLength(attr, CSS_PROP_WIDTH, attr->value());
632 } else if (attr->name() == heightAttr) {
633 if (respectHeightAndWidthAttrs())
634 addCSSLength(attr, CSS_PROP_HEIGHT, attr->value());
635 } else if (attr->name() == onfocusAttr) {
636 setHTMLEventListener(focusEvent, attr);
637 } else if (attr->name() == onblurAttr) {
638 setHTMLEventListener(blurEvent, attr);
639 } else if (attr->name() == onselectAttr) {
640 setHTMLEventListener(selectEvent, attr);
641 } else if (attr->name() == onchangeAttr) {
642 setHTMLEventListener(changeEvent, attr);
643 } else if (attr->name() == oninputAttr) {
644 setHTMLEventListener(inputEvent, attr);
646 // Search field and slider attributes all just cause updateFromElement to be called through style
648 else if (attr->name() == onsearchAttr) {
649 setHTMLEventListener(searchEvent, attr);
650 } else if (attr->name() == resultsAttr) {
651 int oldResults = m_maxResults;
652 m_maxResults = !attr->isNull() ? min(attr->value().toInt(), maxSavedResults) : -1;
653 // FIXME: Detaching just for maxResults change is not ideal. We should figure out the right
654 // time to relayout for this change.
655 if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0) && attached()) {
660 } else if (attr->name() == autosaveAttr ||
661 attr->name() == incrementalAttr ||
662 attr->name() == placeholderAttr ||
663 attr->name() == minAttr ||
664 attr->name() == maxAttr ||
665 attr->name() == precisionAttr) {
668 HTMLFormControlElementWithState::parseMappedAttribute(attr);
671 bool HTMLInputElement::rendererIsNeeded(RenderStyle *style)
673 switch (inputType()) {
686 return HTMLFormControlElementWithState::rendererIsNeeded(style);
694 RenderObject *HTMLInputElement::createRenderer(RenderArena *arena, RenderStyle *style)
696 switch (inputType()) {
700 return new (arena) RenderButton(this);
703 return RenderObject::createObject(this, style);
705 return new (arena) RenderFileUploadControl(this);
709 return new (arena) RenderImage(this);
711 return new (arena) RenderSlider(this);
716 return new (arena) RenderTextControl(this, false);
722 void HTMLInputElement::attach()
726 setInputType(getAttribute(typeAttr));
730 HTMLFormControlElementWithState::attach();
732 if (inputType() == IMAGE) {
734 m_imageLoader = new HTMLImageLoader(this);
735 m_imageLoader->updateFromElement();
737 RenderImage* imageObj = static_cast<RenderImage*>(renderer());
738 imageObj->setCachedImage(m_imageLoader->image());
740 // If we have no image at all because we have no src attribute, set
741 // image height and width for the alt text instead.
742 if (!m_imageLoader->image() && !imageObj->cachedImage())
743 imageObj->setImageSizeForAltText();
748 void HTMLInputElement::detach()
750 HTMLFormControlElementWithState::detach();
751 setValueMatchesRenderer(false);
754 String HTMLInputElement::altText() const
756 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
757 // also heavily discussed by Hixie on bugzilla
758 // note this is intentionally different to HTMLImageElement::altText()
759 String alt = getAttribute(altAttr);
760 // fall back to title attribute
762 alt = getAttribute(titleAttr);
764 alt = getAttribute(valueAttr);
766 alt = inputElementAltText();
770 bool HTMLInputElement::isSuccessfulSubmitButton() const
772 // HTML spec says that buttons must have names to be considered successful.
773 // However, other browsers do not impose this constraint. So we do likewise.
774 return !disabled() && (inputType() == IMAGE || inputType() == SUBMIT);
777 bool HTMLInputElement::isActivatedSubmit() const
779 return m_activeSubmit;
782 void HTMLInputElement::setActivatedSubmit(bool flag)
784 m_activeSubmit = flag;
787 bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart)
789 // image generates its own names, but for other types there is no form data unless there's a name
790 if (name().isEmpty() && inputType() != IMAGE)
793 switch (inputType()) {
801 encoding.appendData(name(), value());
807 encoding.appendData(name(), value());
814 // these types of buttons are never successful
818 if (m_activeSubmit) {
819 encoding.appendData(name().isEmpty() ? "x" : (name() + ".x"), xPos);
820 encoding.appendData(name().isEmpty() ? "y" : (name() + ".y"), yPos);
821 if (!name().isEmpty() && !value().isEmpty())
822 encoding.appendData(name(), value());
828 if (m_activeSubmit) {
829 String enc_str = valueWithDefault();
830 encoding.appendData(name(), enc_str);
836 // Can't submit file on GET.
840 // If no filename at all is entered, return successful but empty.
841 // Null would be more logical, but Netscape posts an empty file. Argh.
842 if (value().isEmpty()) {
843 encoding.appendData(name(), String(""));
847 encoding.appendFile(name(), value());
853 void HTMLInputElement::reset()
855 if (storesValueSeparateFromAttribute())
858 setChecked(m_defaultChecked);
859 m_useDefaultChecked = true;
862 void HTMLInputElement::setChecked(bool nowChecked, bool sendChangeEvent)
864 if (checked() == nowChecked)
867 m_useDefaultChecked = false;
868 m_checked = nowChecked;
871 checkedRadioButtons(this).addButton(this);
873 if (renderer() && renderer()->style()->hasAppearance())
874 theme()->stateChanged(renderer(), CheckedState);
876 // Only send a change event for items in the document (avoid firing during
877 // parsing) and don't send a change event for a radio button that's getting
878 // unchecked to match other browsers. DOM is not a useful standard for this
879 // because it says only to fire change events at "lose focus" time, which is
880 // definitely wrong in practice for these types of elements.
881 if (sendChangeEvent && inDocument() && (inputType() != RADIO || nowChecked))
885 void HTMLInputElement::setIndeterminate(bool _indeterminate)
887 // Only checkboxes honor indeterminate.
888 if (inputType() != CHECKBOX || indeterminate() == _indeterminate)
891 m_indeterminate = _indeterminate;
895 if (renderer() && renderer()->style()->hasAppearance())
896 theme()->stateChanged(renderer(), CheckedState);
899 void HTMLInputElement::copyNonAttributeProperties(const Element *source)
901 const HTMLInputElement *sourceElem = static_cast<const HTMLInputElement *>(source);
903 m_value = sourceElem->m_value;
904 m_checked = sourceElem->m_checked;
905 m_indeterminate = sourceElem->m_indeterminate;
907 HTMLFormControlElementWithState::copyNonAttributeProperties(source);
910 String HTMLInputElement::value() const
912 String value = m_value;
914 // It's important *not* to fall back to the value attribute for file inputs,
915 // because that would allow a malicious web page to upload files by setting the
916 // value attribute in markup.
917 if (value.isNull() && inputType() != FILE)
918 value = constrainValue(getAttribute(valueAttr));
920 // If no attribute exists, then just use "on" or "" based off the checked() state of the control.
921 if (value.isNull() && (inputType() == CHECKBOX || inputType() == RADIO))
922 return checked() ? "on" : "";
927 String HTMLInputElement::valueWithDefault() const
931 switch (inputType()) {
945 v = resetButtonDefaultLabel();
948 v = submitButtonDefaultLabel();
955 void HTMLInputElement::setValue(const String& value)
957 // For security reasons, we don't allow setting the filename, but we do allow clearing it.
958 if (inputType() == FILE && !value.isEmpty())
961 setValueMatchesRenderer(false);
962 if (storesValueSeparateFromAttribute()) {
963 m_value = constrainValue(value);
964 if (isTextField() && inDocument())
965 document()->updateRendering();
967 renderer()->updateFromElement();
970 setAttribute(valueAttr, constrainValue(value));
973 unsigned max = m_value.length();
974 if (document()->focusedNode() == this)
975 setSelectionRange(max, max);
977 cachedSelStart = max;
983 void HTMLInputElement::setValueFromRenderer(const String& value)
985 // Renderer and our event handler are responsible for constraining values.
986 ASSERT(value == constrainValue(value) || constrainValue(value).isEmpty());
988 // Workaround for bug where trailing \n is included in the result of textContent.
989 // The assert macro above may also be simplified to: value == constrainValue(value)
990 // http://bugs.webkit.org/show_bug.cgi?id=9661
996 setValueMatchesRenderer();
998 // Fire the "input" DOM event.
999 dispatchHTMLEvent(inputEvent, true, false);
1002 bool HTMLInputElement::storesValueSeparateFromAttribute() const
1004 switch (inputType()) {
1024 void* HTMLInputElement::preDispatchEventHandler(Event *evt)
1026 // preventDefault or "return false" are used to reverse the automatic checking/selection we do here.
1027 // This result gives us enough info to perform the "undo" in postDispatch of the action we take here.
1029 if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
1030 && evt->type() == clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1031 if (inputType() == CHECKBOX) {
1032 // As a way to store the state, we return 0 if we were unchecked, 1 if we were checked, and 2 for
1034 if (indeterminate()) {
1035 result = (void*)0x2;
1036 setIndeterminate(false);
1039 result = (void*)0x1;
1040 setChecked(!checked(), true);
1043 // For radio buttons, store the current selected radio object.
1044 if (name().isEmpty() || checked())
1045 return 0; // Match WinIE and don't allow unnamed radio buttons to be checked.
1046 // Checked buttons just stay checked.
1047 // FIXME: Need to learn to work without a form.
1049 // We really want radio groups to end up in sane states, i.e., to have something checked.
1050 // Therefore if nothing is currently selected, we won't allow this action to be "undone", since
1051 // we want some object in the radio group to actually get selected.
1052 HTMLInputElement* currRadio = checkedRadioButtons(this).checkedButtonForGroup(name());
1054 // We have a radio button selected that is not us. Cache it in our result field and ref it so
1055 // that it can't be destroyed.
1059 setChecked(true, true);
1065 void HTMLInputElement::postDispatchEventHandler(Event *evt, void* data)
1067 if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
1068 && evt->type() == clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1069 if (inputType() == CHECKBOX) {
1070 // Reverse the checking we did in preDispatch.
1071 if (evt->defaultPrevented() || evt->defaultHandled()) {
1072 if (data == (void*)0x2)
1073 setIndeterminate(true);
1078 HTMLInputElement* input = static_cast<HTMLInputElement*>(data);
1079 if (evt->defaultPrevented() || evt->defaultHandled()) {
1080 // Restore the original selected radio button if possible.
1081 // Make sure it is still a radio button and only do the restoration if it still
1082 // belongs to our group.
1084 // Match WinIE and don't allow unnamed radio buttons to be checked.
1085 if (input->form() == form() && input->inputType() == RADIO && !name().isEmpty() && input->name() == name()) {
1086 // Ok, the old radio button is still in our form and in our group and is still a
1087 // radio button, so it's safe to restore selection to it.
1088 input->setChecked(true);
1094 // Left clicks on radio buttons and check boxes already performed default actions in preDispatchEventHandler().
1095 evt->setDefaultHandled();
1099 void HTMLInputElement::defaultEventHandler(Event* evt)
1101 bool clickDefaultFormButton = false;
1103 if (isTextField() && evt->type() == textInputEvent && evt->isTextEvent() && static_cast<TextEvent*>(evt)->data() == "\n")
1104 clickDefaultFormButton = true;
1106 if (inputType() == IMAGE && evt->isMouseEvent() && evt->type() == clickEvent) {
1107 // record the mouse position for when we get the DOMActivate event
1108 MouseEvent* me = static_cast<MouseEvent*>(evt);
1109 // FIXME: We could just call offsetX() and offsetY() on the event,
1110 // but that's currently broken, so for now do the computation here.
1111 if (me->isSimulated() || !renderer()) {
1115 int offsetX, offsetY;
1116 renderer()->absolutePosition(offsetX, offsetY);
1117 xPos = me->pageX() - offsetX;
1118 // FIXME: Why is yPos a short?
1119 yPos = me->pageY() - offsetY;
1123 // Before calling the base class defaultEventHandler, which will call handleKeypress, call doTextFieldCommandFromEvent.
1124 if (isTextField() && evt->type() == keypressEvent && evt->isKeyboardEvent() && focused() && document()->frame()
1125 && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) {
1126 evt->setDefaultHandled();
1130 if (inputType() == RADIO && evt->isMouseEvent()
1131 && evt->type() == clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1132 evt->setDefaultHandled();
1136 // Let the key handling done in EventTargetNode take precedence over the event handling here for editable text fields
1137 if (!clickDefaultFormButton) {
1138 HTMLFormControlElementWithState::defaultEventHandler(evt);
1139 if (evt->defaultHandled())
1143 // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means
1144 // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks
1145 // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element
1146 // must dispatch a DOMActivate event - a click event will not do the job.
1147 if (evt->type() == DOMActivateEvent && !disabled()) {
1148 if (inputType() == IMAGE || inputType() == SUBMIT || inputType() == RESET) {
1151 if (inputType() == RESET)
1154 m_activeSubmit = true;
1155 // FIXME: Would be cleaner to get xPos and yPos out of the underlying mouse
1156 // event (if any) here instead of relying on the variables set above when
1157 // processing the click event. Even better, appendFormData could pass the
1158 // event in, and then we could get rid of xPos and yPos altogether!
1159 if (!form()->prepareSubmit(evt)) {
1163 m_activeSubmit = false;
1165 } else if (inputType() == FILE && renderer())
1166 static_cast<RenderFileUploadControl*>(renderer())->click();
1169 // Use key press event here since sending simulated mouse events
1170 // on key down blocks the proper sending of the key press event.
1171 if (evt->type() == keypressEvent && evt->isKeyboardEvent()) {
1172 bool clickElement = false;
1174 String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
1176 if (key == "U+0020") {
1177 switch (inputType()) {
1184 // Simulate mouse click for spacebar for these types of elements.
1185 // The AppKit already does this for some, but not all, of them.
1186 clickElement = true;
1189 // If an unselected radio is tabbed into (because the entire group has nothing
1190 // checked, or because of some explicit .focus() call), then allow space to check it.
1192 clickElement = true;
1204 if (key == "Enter") {
1205 switch (inputType()) {
1214 // Simulate mouse click on the default form button for enter for these types of elements.
1215 clickDefaultFormButton = true;
1221 // Simulate mouse click for enter for these types of elements.
1222 clickElement = true;
1225 break; // Don't do anything for enter on a radio button.
1229 if (inputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) {
1230 // Left and up mean "previous radio button".
1231 // Right and down mean "next radio button".
1232 // Tested in WinIE, and even for RTL, left still means previous radio button (and so moves
1233 // to the right). Seems strange, but we'll match it.
1234 bool forward = (key == "Down" || key == "Right");
1236 // We can only stay within the form's children if the form hasn't been demoted to a leaf because
1237 // of malformed HTML.
1239 while ((n = (forward ? n->traverseNextNode() : n->traversePreviousNode()))) {
1240 // Once we encounter a form element, we know we're through.
1241 if (n->hasTagName(formTag))
1244 // Look for more radio buttons.
1245 if (n->hasTagName(inputTag)) {
1246 HTMLInputElement* elt = static_cast<HTMLInputElement*>(n);
1247 if (elt->form() != form())
1249 if (n->hasTagName(inputTag)) {
1250 // Match WinIE and don't allow unnamed radio buttons to be checked.
1251 HTMLInputElement* inputElt = static_cast<HTMLInputElement*>(n);
1252 if (inputElt->inputType() == RADIO && !name().isEmpty() && inputElt->name() == name() && inputElt->isFocusable()) {
1253 inputElt->setChecked(true);
1254 document()->setFocusedNode(inputElt);
1255 inputElt->dispatchSimulatedClick(evt, false, false);
1256 evt->setDefaultHandled();
1265 dispatchSimulatedClick(evt);
1266 evt->setDefaultHandled();
1271 if (clickDefaultFormButton) {
1272 if (isSearchField()) {
1276 // Fire onChange for text fields.
1277 RenderObject* r = renderer();
1278 if (r && r->isTextField() && r->isEdited()) {
1280 // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it.
1283 r->setEdited(false);
1285 // Form may never have been present, or may have been destroyed by the change event.
1287 form()->submitClick(evt);
1288 evt->setDefaultHandled();
1292 if (evt->isBeforeTextInsertedEvent()) {
1293 // Make sure that the text to be inserted will not violate the maxLength.
1294 int oldLen = numGraphemeClusters(value().impl());
1295 ASSERT(oldLen <= maxLength());
1296 int selectionLen = numGraphemeClusters(document()->frame()->selectionController()->toString().impl());
1297 ASSERT(oldLen >= selectionLen);
1298 int maxNewLen = maxLength() - (oldLen - selectionLen);
1300 // Truncate the inserted text to avoid violating the maxLength and other constraints.
1301 BeforeTextInsertedEvent* textEvent = static_cast<BeforeTextInsertedEvent*>(evt);
1302 textEvent->setText(constrainValue(textEvent->text(), maxNewLen));
1305 if (isTextField() && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == blurEvent || evt->type() == focusEvent))
1306 static_cast<RenderTextControl*>(renderer())->forwardEvent(evt);
1308 if (inputType() == RANGE && renderer()) {
1309 RenderSlider* slider = static_cast<RenderSlider*>(renderer());
1310 if (evt->isMouseEvent() && evt->type() == mousedownEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1311 MouseEvent* mEvt = static_cast<MouseEvent*>(evt);
1312 if (!slider->mouseEventIsInThumb(mEvt)) {
1313 slider->setValueForPosition(slider->positionForOffset(IntPoint(mEvt->offsetX(), mEvt->offsetY())));
1316 if (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent())
1317 slider->forwardEvent(evt);
1321 bool HTMLInputElement::isURLAttribute(Attribute *attr) const
1323 return (attr->name() == srcAttr);
1326 String HTMLInputElement::defaultValue() const
1328 return getAttribute(valueAttr);
1331 void HTMLInputElement::setDefaultValue(const String &value)
1333 setAttribute(valueAttr, value);
1336 bool HTMLInputElement::defaultChecked() const
1338 return !getAttribute(checkedAttr).isNull();
1341 void HTMLInputElement::setDefaultChecked(bool defaultChecked)
1343 setAttribute(checkedAttr, defaultChecked ? "" : 0);
1346 String HTMLInputElement::accept() const
1348 return getAttribute(acceptAttr);
1351 void HTMLInputElement::setAccept(const String &value)
1353 setAttribute(acceptAttr, value);
1356 String HTMLInputElement::accessKey() const
1358 return getAttribute(accesskeyAttr);
1361 void HTMLInputElement::setAccessKey(const String &value)
1363 setAttribute(accesskeyAttr, value);
1366 String HTMLInputElement::align() const
1368 return getAttribute(alignAttr);
1371 void HTMLInputElement::setAlign(const String &value)
1373 setAttribute(alignAttr, value);
1376 String HTMLInputElement::alt() const
1378 return getAttribute(altAttr);
1381 void HTMLInputElement::setAlt(const String &value)
1383 setAttribute(altAttr, value);
1386 void HTMLInputElement::setMaxLength(int _maxLength)
1388 setAttribute(maxlengthAttr, String::number(_maxLength));
1391 void HTMLInputElement::setSize(unsigned _size)
1393 setAttribute(sizeAttr, String::number(_size));
1396 String HTMLInputElement::src() const
1398 return document()->completeURL(getAttribute(srcAttr));
1401 void HTMLInputElement::setSrc(const String &value)
1403 setAttribute(srcAttr, value);
1406 String HTMLInputElement::useMap() const
1408 return getAttribute(usemapAttr);
1411 void HTMLInputElement::setUseMap(const String &value)
1413 setAttribute(usemapAttr, value);
1416 String HTMLInputElement::constrainValue(const String& proposedValue) const
1418 return constrainValue(proposedValue, m_maxLen);
1421 void HTMLInputElement::recheckValue()
1423 String oldValue = value();
1424 String newValue = constrainValue(oldValue);
1425 if (newValue != oldValue)
1429 String HTMLInputElement::constrainValue(const String& proposedValue, int maxLen) const
1431 if (isTextField()) {
1432 StringImpl* s = proposedValue.impl();
1433 int newLen = numCharactersInGraphemeClusters(s, maxLen);
1434 for (int i = 0; i < newLen; ++i) {
1435 const UChar current = (*s)[i];
1436 if (current < ' ' && current != '\t') {
1441 if (newLen < static_cast<int>(proposedValue.length()))
1442 return proposedValue.substring(0, newLen);
1444 return proposedValue;
1447 void HTMLInputElement::addSearchResult()
1449 ASSERT(isSearchField());
1451 static_cast<RenderTextControl*>(renderer())->addSearchResult();
1454 void HTMLInputElement::onSearch()
1456 ASSERT(isSearchField());
1458 static_cast<RenderTextControl*>(renderer())->stopSearchEventTimer();
1459 dispatchHTMLEvent(searchEvent, true, false);
1462 Selection HTMLInputElement::selection() const
1464 if (!renderer() || !isTextField() || cachedSelStart == -1 || cachedSelEnd == -1)
1466 return static_cast<RenderTextControl*>(renderer())->selection(cachedSelStart, cachedSelEnd);
1469 void HTMLInputElement::didRestoreFromCache()
1471 ASSERT(inputType() == PASSWORD);
1475 void HTMLInputElement::willMoveToNewOwnerDocument()
1477 if (inputType() == PASSWORD)
1478 document()->unregisterForCacheCallbacks(this);
1480 document()->checkedRadioButtons().removeButton(this);
1482 HTMLFormControlElementWithState::willMoveToNewOwnerDocument();
1485 void HTMLInputElement::didMoveToNewOwnerDocument()
1487 if (inputType() == PASSWORD)
1488 document()->registerForCacheCallbacks(this);
1490 HTMLFormControlElementWithState::didMoveToNewOwnerDocument();