2 * This file is part of the DOM implementation for KDE.
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * (C) 2001 Dirk Mueller (mueller@kde.org)
7 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
8 * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 * Boston, MA 02111-1307, USA.
28 #include "HTMLInputElement.h"
30 #include "BeforeTextInsertedEvent.h"
31 #include "CSSPropertyNames.h"
34 #include "EventHandler.h"
35 #include "EventNames.h"
36 #include "FormDataList.h"
38 #include "HTMLFormElement.h"
39 #include "HTMLImageLoader.h"
40 #include "HTMLNames.h"
41 #include "KeyboardEvent.h"
42 #include "LocalizedStrings.h"
43 #include "MouseEvent.h"
44 #include "RenderButton.h"
45 #include "RenderFileUploadControl.h"
46 #include "RenderImage.h"
47 #include "RenderText.h"
48 #include "RenderTextControl.h"
49 #include "RenderTheme.h"
50 #include "RenderSlider.h"
51 #include "SelectionController.h"
52 #include "TextBreakIterator.h"
58 using namespace EventNames;
59 using namespace HTMLNames;
61 static int numGraphemeClusters(const StringImpl* s)
66 TextBreakIterator* it = characterBreakIterator(s->characters(), s->length());
70 while (textBreakNext(it) != TextBreakDone)
75 static int numCharactersInGraphemeClusters(const StringImpl* s, int numGraphemeClusters)
80 TextBreakIterator* it = characterBreakIterator(s->characters(), s->length());
83 for (int i = 0; i < numGraphemeClusters; ++i)
84 if (textBreakNext(it) == TextBreakDone)
86 return textBreakCurrent(it);
89 HTMLInputElement::HTMLInputElement(Document *doc, HTMLFormElement *f)
90 : HTMLGenericFormElement(inputTag, doc, f)
95 HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document *doc, HTMLFormElement *f)
96 : HTMLGenericFormElement(tagName, doc, f)
101 void HTMLInputElement::init()
108 m_defaultChecked = false;
109 m_useDefaultChecked = true;
110 m_indeterminate = false;
113 m_activeSubmit = false;
114 m_autocomplete = true;
116 m_autofilled = false;
127 m_autocomplete = form()->autoComplete();
129 document()->registerFormElementWithState(this);
132 HTMLInputElement::~HTMLInputElement()
134 document()->deregisterFormElementWithState(this);
135 delete m_imageLoader;
138 const AtomicString& HTMLInputElement::name() const
140 return m_name.isNull() ? emptyAtom : m_name;
143 bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const
145 // If text fields can be focused, then they should always be keyboard focusable
147 return HTMLGenericFormElement::isFocusable();
149 // If the base class says we can't be focused, then we can stop now.
150 if (!HTMLGenericFormElement::isKeyboardFocusable(event))
153 if (inputType() == RADIO) {
154 // Unnamed radio buttons are never focusable (matches WinIE).
155 if (name().isEmpty())
158 // Never allow keyboard tabbing to leave you in the same radio group. Always
159 // skip any other elements in the group.
160 Node* currentFocusedNode = document()->focusedNode();
161 if (currentFocusedNode && currentFocusedNode->hasTagName(inputTag)) {
162 HTMLInputElement* focusedInput = static_cast<HTMLInputElement*>(currentFocusedNode);
163 if (focusedInput->inputType() == RADIO && focusedInput->form() == form() &&
164 focusedInput->name() == name())
168 // Allow keyboard focus if we're checked or if nothing in the group is checked.
169 return checked() || !document()->checkedRadioButtonForGroup(name().impl(), form());
175 bool HTMLInputElement::isMouseFocusable() const
178 return HTMLGenericFormElement::isFocusable();
179 return HTMLGenericFormElement::isMouseFocusable();
182 void HTMLInputElement::focus()
185 Document* doc = document();
186 if (doc->focusedNode() == this)
189 if (!supportsFocus())
191 doc->setFocusedNode(this);
192 // FIXME: Should isFocusable do the updateLayout?
193 if (!isFocusable()) {
194 setNeedsFocusAppearanceUpdate(true);
197 updateFocusAppearance();
200 HTMLGenericFormElement::focus();
203 void HTMLInputElement::updateFocusAppearance()
207 if (document() && document()->frame())
208 document()->frame()->revealSelection();
210 HTMLGenericFormElement::updateFocusAppearance();
213 void HTMLInputElement::aboutToUnload()
215 if (isTextField() && document()->frame())
216 document()->frame()->textFieldDidEndEditing(this);
219 void HTMLInputElement::dispatchFocusEvent()
222 setAutofilled(false);
223 if (inputType() == PASSWORD && document()->frame())
224 document()->frame()->setSecureKeyboardEntry(true);
226 HTMLGenericFormElement::dispatchFocusEvent();
229 void HTMLInputElement::dispatchBlurEvent()
231 if (isTextField() && document()->frame()) {
232 if (inputType() == PASSWORD)
233 document()->frame()->setSecureKeyboardEntry(false);
234 document()->frame()->textFieldDidEndEditing(static_cast<Element*>(this));
236 HTMLGenericFormElement::dispatchBlurEvent();
239 void HTMLInputElement::setType(const String& t)
243 removeAttribute(typeAttr, exccode);
245 setAttribute(typeAttr, t);
248 void HTMLInputElement::setInputType(const String& t)
252 if (equalIgnoringCase(t, "password"))
254 else if (equalIgnoringCase(t, "checkbox"))
256 else if (equalIgnoringCase(t, "radio"))
258 else if (equalIgnoringCase(t, "submit"))
260 else if (equalIgnoringCase(t, "reset"))
262 else if (equalIgnoringCase(t, "file"))
264 else if (equalIgnoringCase(t, "hidden"))
266 else if (equalIgnoringCase(t, "image"))
268 else if (equalIgnoringCase(t, "button"))
270 else if (equalIgnoringCase(t, "khtml_isindex"))
272 else if (equalIgnoringCase(t, "search"))
274 else if (equalIgnoringCase(t, "range"))
279 // IMPORTANT: Don't allow the type to be changed to FILE after the first
280 // type change, otherwise a JavaScript programmer would be able to set a text
281 // field's value to something like /etc/passwd and then change it to a file field.
282 if (inputType() != newType) {
283 if (newType == FILE && m_haveType)
284 // Set the attribute back to the old value.
285 // Useful in case we were called from inside parseMappedAttribute.
286 setAttribute(typeAttr, type());
288 if (inputType() == RADIO && !name().isEmpty())
289 if (document()->checkedRadioButtonForGroup(name().impl(), form()) == this)
290 document()->removeRadioButtonGroup(name().impl(), form());
292 bool wasAttached = m_attached;
296 bool didStoreValue = storesValueSeparateFromAttribute();
297 bool didMaintainState = inputType() != PASSWORD;
298 bool didRespectHeightAndWidth = respectHeightAndWidthAttrs();
300 bool willStoreValue = storesValueSeparateFromAttribute();
301 bool willMaintainState = inputType() != PASSWORD;
302 bool willRespectHeightAndWidth = respectHeightAndWidthAttrs();
304 if (didStoreValue && !willStoreValue && !m_value.isNull()) {
305 setAttribute(valueAttr, m_value);
308 if (!didStoreValue && willStoreValue)
309 m_value = constrainValue(getAttribute(valueAttr));
313 if (willMaintainState && !didMaintainState)
314 document()->registerFormElementWithState(this);
315 else if (!willMaintainState && didMaintainState)
316 document()->deregisterFormElementWithState(this);
318 if (didRespectHeightAndWidth != willRespectHeightAndWidth) {
319 NamedMappedAttrMap* map = mappedAttributes();
320 if (MappedAttribute* height = map->getAttributeItem(heightAttr))
321 attributeChanged(height, false);
322 if (MappedAttribute* width = map->getAttributeItem(widthAttr))
323 attributeChanged(width, false);
329 // If our type morphs into a radio button and we are checked, then go ahead
330 // and signal this to the form.
331 if (inputType() == RADIO && checked())
332 document()->radioButtonChecked(this, form());
337 if (inputType() != IMAGE && m_imageLoader) {
338 delete m_imageLoader;
343 const AtomicString& HTMLInputElement::type() const
345 // needs to be lowercase according to DOM spec
346 switch (inputType()) {
348 static const AtomicString button("button");
352 static const AtomicString checkbox("checkbox");
356 static const AtomicString file("file");
360 static const AtomicString hidden("hidden");
364 static const AtomicString image("image");
370 static const AtomicString password("password");
374 static const AtomicString radio("radio");
378 static const AtomicString range("range");
382 static const AtomicString reset("reset");
386 static const AtomicString search("search");
390 static const AtomicString submit("submit");
394 static const AtomicString text("text");
401 String HTMLInputElement::stateValue() const
403 ASSERT(inputType() != PASSWORD); // should never save/restore password fields
404 switch (inputType()) {
418 return checked() ? "on" : "off";
425 void HTMLInputElement::restoreState(const String& state)
427 ASSERT(inputType() != PASSWORD); // should never save/restore password fields
428 switch (inputType()) {
443 setChecked(state == "on");
450 bool HTMLInputElement::canHaveSelection() const
452 switch (inputType()) {
472 int HTMLInputElement::selectionStart() const
477 switch (inputType()) {
492 if (document()->focusedNode() != this && cachedSelStart >= 0)
493 return cachedSelStart;
494 return static_cast<RenderTextControl*>(renderer())->selectionStart();
499 int HTMLInputElement::selectionEnd() const
504 switch (inputType()) {
519 if (document()->focusedNode() != this && cachedSelEnd >= 0)
521 return static_cast<RenderTextControl*>(renderer())->selectionEnd();
526 void HTMLInputElement::setSelectionStart(int start)
531 switch (inputType()) {
546 static_cast<RenderTextControl*>(renderer())->setSelectionStart(start);
551 void HTMLInputElement::setSelectionEnd(int end)
556 switch (inputType()) {
571 static_cast<RenderTextControl*>(renderer())->setSelectionEnd(end);
576 void HTMLInputElement::select()
581 switch (inputType()) {
596 static_cast<RenderTextControl*>(renderer())->select();
601 void HTMLInputElement::setSelectionRange(int start, int end)
606 switch (inputType()) {
621 static_cast<RenderTextControl*>(renderer())->setSelectionRange(start, end);
626 void HTMLInputElement::accessKeyAction(bool sendToAnyElement)
628 switch (inputType()) {
639 // send the mouse button events iff the caller specified sendToAnyElement
640 dispatchSimulatedClick(0, sendToAnyElement);
643 // a no-op for this type
654 bool HTMLInputElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
656 if (((attrName == heightAttr || attrName == widthAttr) && respectHeightAndWidthAttrs()) ||
657 attrName == vspaceAttr ||
658 attrName == hspaceAttr) {
663 if (attrName == alignAttr) {
664 result = eReplaced; // Share with <img> since the alignment behavior is the same.
668 return HTMLElement::mapToEntry(attrName, result);
671 void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr)
673 if (attr->name() == nameAttr) {
674 if (inputType() == RADIO && checked()) {
675 // Remove the radio from its old group.
676 if (!m_name.isEmpty())
677 document()->removeRadioButtonGroup(m_name.impl(), form());
680 // Update our cached reference to the name.
681 m_name = attr->value();
683 if (inputType() == RADIO) {
684 // In case we parsed the checked attribute first, call setChecked if the element is checked by default.
685 if (m_useDefaultChecked)
686 setChecked(m_defaultChecked);
687 // Add the button to its new group.
689 document()->radioButtonChecked(this, form());
691 } else if (attr->name() == autocompleteAttr) {
692 m_autocomplete = !equalIgnoringCase(attr->value(), "off");
693 } else if (attr->name() == typeAttr) {
694 setInputType(attr->value());
695 } else if (attr->name() == valueAttr) {
696 // We only need to setChanged if the form is looking at the default value right now.
697 if (m_value.isNull())
699 setValueMatchesRenderer(false);
700 } else if (attr->name() == checkedAttr) {
701 m_defaultChecked = !attr->isNull();
702 if (m_useDefaultChecked) {
703 setChecked(m_defaultChecked);
704 m_useDefaultChecked = true;
706 } else if (attr->name() == maxlengthAttr) {
707 int oldMaxLen = m_maxLen;
708 m_maxLen = !attr->isNull() ? attr->value().toInt() : 1024;
709 if (m_maxLen <= 0 || m_maxLen > 1024)
711 if (oldMaxLen != m_maxLen)
714 } else if (attr->name() == sizeAttr) {
715 m_size = !attr->isNull() ? attr->value().toInt() : 20;
716 } else if (attr->name() == altAttr) {
717 if (renderer() && inputType() == IMAGE)
718 static_cast<RenderImage*>(renderer())->updateAltText();
719 } else if (attr->name() == srcAttr) {
720 if (renderer() && inputType() == IMAGE) {
722 m_imageLoader = new HTMLImageLoader(this);
723 m_imageLoader->updateFromElement();
725 } else if (attr->name() == usemapAttr ||
726 attr->name() == accesskeyAttr) {
727 // FIXME: ignore for the moment
728 } else if (attr->name() == vspaceAttr) {
729 addCSSLength(attr, CSS_PROP_MARGIN_TOP, attr->value());
730 addCSSLength(attr, CSS_PROP_MARGIN_BOTTOM, attr->value());
731 } else if (attr->name() == hspaceAttr) {
732 addCSSLength(attr, CSS_PROP_MARGIN_LEFT, attr->value());
733 addCSSLength(attr, CSS_PROP_MARGIN_RIGHT, attr->value());
734 } else if (attr->name() == alignAttr) {
735 addHTMLAlignment(attr);
736 } else if (attr->name() == widthAttr) {
737 if (respectHeightAndWidthAttrs())
738 addCSSLength(attr, CSS_PROP_WIDTH, attr->value());
739 } else if (attr->name() == heightAttr) {
740 if (respectHeightAndWidthAttrs())
741 addCSSLength(attr, CSS_PROP_HEIGHT, attr->value());
742 } else if (attr->name() == onfocusAttr) {
743 setHTMLEventListener(focusEvent, attr);
744 } else if (attr->name() == onblurAttr) {
745 setHTMLEventListener(blurEvent, attr);
746 } else if (attr->name() == onselectAttr) {
747 setHTMLEventListener(selectEvent, attr);
748 } else if (attr->name() == onchangeAttr) {
749 setHTMLEventListener(changeEvent, attr);
750 } else if (attr->name() == oninputAttr) {
751 setHTMLEventListener(inputEvent, attr);
753 // Search field and slider attributes all just cause updateFromElement to be called through style
755 else if (attr->name() == onsearchAttr) {
756 setHTMLEventListener(searchEvent, attr);
757 } else if (attr->name() == resultsAttr) {
758 int oldResults = m_maxResults;
759 m_maxResults = !attr->isNull() ? attr->value().toInt() : -1;
760 // FIXME: Detaching just for maxResults change is not ideal. We should figure out the right
761 // time to relayout for this change.
762 if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0) && attached()) {
767 } else if (attr->name() == autosaveAttr ||
768 attr->name() == incrementalAttr ||
769 attr->name() == placeholderAttr ||
770 attr->name() == minAttr ||
771 attr->name() == maxAttr ||
772 attr->name() == precisionAttr) {
775 HTMLGenericFormElement::parseMappedAttribute(attr);
778 bool HTMLInputElement::rendererIsNeeded(RenderStyle *style)
780 switch (inputType()) {
793 return HTMLGenericFormElement::rendererIsNeeded(style);
801 RenderObject *HTMLInputElement::createRenderer(RenderArena *arena, RenderStyle *style)
803 switch (inputType()) {
807 return new (arena) RenderButton(this);
810 return RenderObject::createObject(this, style);
812 return new (arena) RenderFileUploadControl(this);
816 return new (arena) RenderImage(this);
818 return new (arena) RenderSlider(this);
823 return new (arena) RenderTextControl(this, false);
829 void HTMLInputElement::attach()
833 setInputType(getAttribute(typeAttr));
837 HTMLGenericFormElement::attach();
839 if (inputType() == IMAGE) {
841 m_imageLoader = new HTMLImageLoader(this);
842 m_imageLoader->updateFromElement();
844 RenderImage* imageObj = static_cast<RenderImage*>(renderer());
845 imageObj->setCachedImage(m_imageLoader->image());
849 // note we don't deal with calling passwordFieldRemoved() on detach, because the timing
850 // was such that it cleared our state too early
851 if (inputType() == PASSWORD)
852 document()->passwordFieldAdded();
855 void HTMLInputElement::detach()
857 HTMLGenericFormElement::detach();
858 setValueMatchesRenderer(false);
861 String HTMLInputElement::altText() const
863 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
864 // also heavily discussed by Hixie on bugzilla
865 // note this is intentionally different to HTMLImageElement::altText()
866 String alt = getAttribute(altAttr);
867 // fall back to title attribute
869 alt = getAttribute(titleAttr);
871 alt = getAttribute(valueAttr);
873 alt = inputElementAltText();
877 bool HTMLInputElement::isSuccessfulSubmitButton() const
879 // HTML spec says that buttons must have names to be considered successful.
880 // However, other browsers do not impose this constraint. So we do likewise.
881 return !disabled() && (inputType() == IMAGE || inputType() == SUBMIT);
884 bool HTMLInputElement::isActivatedSubmit() const
886 return m_activeSubmit;
889 void HTMLInputElement::setActivatedSubmit(bool flag)
891 m_activeSubmit = flag;
894 bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart)
896 // image generates its own names, but for other types there is no form data unless there's a name
897 if (name().isEmpty() && inputType() != IMAGE)
900 switch (inputType()) {
908 encoding.appendData(name(), value());
914 encoding.appendData(name(), value());
921 // these types of buttons are never successful
925 if (m_activeSubmit) {
926 encoding.appendData(name().isEmpty() ? "x" : (name() + ".x"), clickX());
927 encoding.appendData(name().isEmpty() ? "y" : (name() + ".y"), clickY());
928 if (!name().isEmpty() && !value().isEmpty())
929 encoding.appendData(name(), value());
935 if (m_activeSubmit) {
936 String enc_str = valueWithDefault();
937 if (!enc_str.isEmpty()) {
938 encoding.appendData(name(), enc_str);
945 // Can't submit file on GET.
946 // Don't submit if display: none or display: hidden to avoid uploading files quietly.
947 if (!multipart || !renderer() || renderer()->style()->visibility() != VISIBLE)
950 // If no filename at all is entered, return successful but empty.
951 // Null would be more logical, but Netscape posts an empty file. Argh.
952 if (value().isEmpty()) {
953 encoding.appendData(name(), String(""));
957 encoding.appendFile(name(), value());
963 void HTMLInputElement::reset()
965 if (storesValueSeparateFromAttribute())
967 setChecked(m_defaultChecked);
968 m_useDefaultChecked = true;
971 void HTMLInputElement::setChecked(bool nowChecked, bool sendChangeEvent)
973 // We mimic WinIE and don't allow unnamed radio buttons to be checked.
974 if (checked() == nowChecked || (inputType() == RADIO && name().isEmpty()))
977 if (inputType() == RADIO && nowChecked)
978 document()->radioButtonChecked(this, form());
980 m_useDefaultChecked = false;
981 m_checked = nowChecked;
983 if (renderer() && renderer()->style()->hasAppearance())
984 theme()->stateChanged(renderer(), CheckedState);
986 // Only send a change event for items in the document (avoid firing during
987 // parsing) and don't send a change event for a radio button that's getting
988 // unchecked to match other browsers. DOM is not a useful standard for this
989 // because it says only to fire change events at "lose focus" time, which is
990 // definitely wrong in practice for these types of elements.
991 if (sendChangeEvent && inDocument() && (inputType() != RADIO || nowChecked))
995 void HTMLInputElement::setIndeterminate(bool _indeterminate)
997 // Only checkboxes honor indeterminate.
998 if (inputType() != CHECKBOX || indeterminate() == _indeterminate)
1001 m_indeterminate = _indeterminate;
1005 if (renderer() && renderer()->style()->hasAppearance())
1006 theme()->stateChanged(renderer(), CheckedState);
1009 void HTMLInputElement::copyNonAttributeProperties(const Element *source)
1011 const HTMLInputElement *sourceElem = static_cast<const HTMLInputElement *>(source);
1013 m_value = sourceElem->m_value;
1014 m_checked = sourceElem->m_checked;
1015 m_indeterminate = sourceElem->m_indeterminate;
1018 String HTMLInputElement::value() const
1020 String value = m_value;
1022 // It's important *not* to fall back to the value attribute for file inputs,
1023 // because that would allow a malicious web page to upload files by setting the
1024 // value attribute in markup.
1025 if (value.isNull() && inputType() != FILE)
1026 value = constrainValue(getAttribute(valueAttr));
1028 // If no attribute exists, then just use "on" or "" based off the checked() state of the control.
1029 if (value.isNull() && (inputType() == CHECKBOX || inputType() == RADIO))
1030 return checked() ? "on" : "";
1035 String HTMLInputElement::valueWithDefault() const
1039 switch (inputType()) {
1053 v = resetButtonDefaultLabel();
1056 v = submitButtonDefaultLabel();
1063 void HTMLInputElement::setValue(const String& value)
1065 if (inputType() == FILE)
1068 setValueMatchesRenderer(false);
1069 if (storesValueSeparateFromAttribute()) {
1070 m_value = constrainValue(value);
1072 renderer()->updateFromElement();
1073 // Changes to hidden values don't require re-rendering.
1074 if (m_type != HIDDEN)
1077 setAttribute(valueAttr, constrainValue(value));
1079 // Restore a caret at the starting point of the old selection.
1080 // This matches Safari 2.0 behavior.
1081 if (isTextField() && document()->focusedNode() == this && cachedSelStart >= 0) {
1082 ASSERT(cachedSelEnd >= 0);
1083 setSelectionRange(cachedSelStart, cachedSelStart);
1087 void HTMLInputElement::setValueFromRenderer(const String& value)
1089 // Renderer and our event handler are responsible for constraining values.
1090 ASSERT(value == constrainValue(value) || constrainValue(value).isEmpty());
1092 // Workaround for bug where trailing \n is included in the result of textContent.
1093 // The assert macro above may also be simplified to: value == constrainValue(value)
1094 // http://bugs.webkit.org/show_bug.cgi?id=9661
1100 setValueMatchesRenderer();
1102 // Fire the "input" DOM event.
1103 dispatchHTMLEvent(inputEvent, true, false);
1106 bool HTMLInputElement::storesValueSeparateFromAttribute() const
1108 switch (inputType()) {
1128 void* HTMLInputElement::preDispatchEventHandler(Event *evt)
1130 // preventDefault or "return false" are used to reverse the automatic checking/selection we do here.
1131 // This result gives us enough info to perform the "undo" in postDispatch of the action we take here.
1133 if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
1134 && evt->type() == clickEvent && static_cast<MouseEvent*>(evt)->button() == 0) {
1135 if (inputType() == CHECKBOX) {
1136 // As a way to store the state, we return 0 if we were unchecked, 1 if we were checked, and 2 for
1138 if (indeterminate()) {
1139 result = (void*)0x2;
1140 setIndeterminate(false);
1143 result = (void*)0x1;
1144 setChecked(!checked(), true);
1147 // For radio buttons, store the current selected radio object.
1148 if (name().isEmpty() || checked())
1149 return 0; // Unnamed radio buttons dont get checked. Checked buttons just stay checked.
1150 // FIXME: Need to learn to work without a form.
1152 // We really want radio groups to end up in sane states, i.e., to have something checked.
1153 // Therefore if nothing is currently selected, we won't allow this action to be "undone", since
1154 // we want some object in the radio group to actually get selected.
1155 HTMLInputElement* currRadio = document()->checkedRadioButtonForGroup(name().impl(), form());
1157 // We have a radio button selected that is not us. Cache it in our result field and ref it so
1158 // that it can't be destroyed.
1162 setChecked(true, true);
1168 void HTMLInputElement::postDispatchEventHandler(Event *evt, void* data)
1170 if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
1171 && evt->type() == clickEvent && static_cast<MouseEvent*>(evt)->button() == 0) {
1172 if (inputType() == CHECKBOX) {
1173 // Reverse the checking we did in preDispatch.
1174 if (evt->defaultPrevented() || evt->defaultHandled()) {
1175 if (data == (void*)0x2)
1176 setIndeterminate(true);
1181 HTMLInputElement* input = static_cast<HTMLInputElement*>(data);
1182 if (evt->defaultPrevented() || evt->defaultHandled()) {
1183 // Restore the original selected radio button if possible.
1184 // Make sure it is still a radio button and only do the restoration if it still
1185 // belongs to our group.
1186 if (input->form() == form() && input->inputType() == RADIO && input->name() == name()) {
1187 // Ok, the old radio button is still in our form and in our group and is still a
1188 // radio button, so it's safe to restore selection to it.
1189 input->setChecked(true);
1197 void HTMLInputElement::defaultEventHandler(Event* evt)
1199 if (inputType() == IMAGE && evt->isMouseEvent() && evt->type() == clickEvent) {
1200 // record the mouse position for when we get the DOMActivate event
1201 MouseEvent* me = static_cast<MouseEvent*>(evt);
1202 // FIXME: We could just call offsetX() and offsetY() on the event,
1203 // but that's currently broken, so for now do the computation here.
1204 if (me->isSimulated() || !renderer()) {
1208 int offsetX, offsetY;
1209 renderer()->absolutePosition(offsetX, offsetY);
1210 xPos = me->pageX() - offsetX;
1211 yPos = me->pageY() - offsetY;
1213 me->setDefaultHandled();
1216 // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means
1217 // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks
1218 // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element
1219 // must dispatch a DOMActivate event - a click event will not do the job.
1220 if (evt->type() == DOMActivateEvent && !disabled()) {
1221 if (inputType() == IMAGE || inputType() == SUBMIT || inputType() == RESET) {
1224 if (inputType() == RESET)
1227 m_activeSubmit = true;
1228 if (!form()->prepareSubmit(evt)) {
1232 m_activeSubmit = false;
1234 } else if (inputType() == FILE && renderer())
1235 static_cast<RenderFileUploadControl*>(renderer())->click();
1238 // Use key press event here since sending simulated mouse events
1239 // on key down blocks the proper sending of the key press event.
1240 if (evt->type() == keypressEvent && evt->isKeyboardEvent()) {
1241 bool clickElement = false;
1242 bool clickDefaultFormButton = false;
1244 if (isTextField() && document()->frame()
1245 && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) {
1246 evt->setDefaultHandled();
1250 String key = static_cast<KeyboardEvent *>(evt)->keyIdentifier();
1252 if (key == "U+000020") {
1253 switch (inputType()) {
1260 // Simulate mouse click for spacebar for these types of elements.
1261 // The AppKit already does this for some, but not all, of them.
1262 clickElement = true;
1265 // If an unselected radio is tabbed into (because the entire group has nothing
1266 // checked, or because of some explicit .focus() call), then allow space to check it.
1268 clickElement = true;
1280 if (key == "Enter") {
1281 switch (inputType()) {
1286 // Simulate mouse click on the default form button for enter for these types of elements.
1287 clickDefaultFormButton = true;
1292 if (!document()->frame()->eventHandler()->inputManagerHasMarkedText())
1293 // Simulate mouse click on the default form button for enter for these types of elements.
1294 clickDefaultFormButton = true;
1300 // Simulate mouse click for enter for these types of elements.
1301 clickElement = true;
1304 break; // Don't do anything for enter on a radio button.
1308 if (inputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) {
1309 // Left and up mean "previous radio button".
1310 // Right and down mean "next radio button".
1311 // Tested in WinIE, and even for RTL, left still means previous radio button (and so moves
1312 // to the right). Seems strange, but we'll match it.
1313 bool forward = (key == "Down" || key == "Right");
1315 // We can only stay within the form's children if the form hasn't been demoted to a leaf because
1316 // of malformed HTML.
1318 while ((n = (forward ? n->traverseNextNode() : n->traversePreviousNode()))) {
1319 // Once we encounter a form element, we know we're through.
1320 if (n->hasTagName(formTag))
1323 // Look for more radio buttons.
1324 if (n->hasTagName(inputTag)) {
1325 HTMLInputElement* elt = static_cast<HTMLInputElement*>(n);
1326 if (elt->form() != form())
1328 if (n->hasTagName(inputTag)) {
1329 HTMLInputElement* inputElt = static_cast<HTMLInputElement*>(n);
1330 if (inputElt->inputType() == RADIO && inputElt->name() == name() &&
1331 inputElt->isFocusable()) {
1332 inputElt->setChecked(true);
1333 document()->setFocusedNode(inputElt);
1334 inputElt->dispatchSimulatedClick(evt, false, false);
1335 evt->setDefaultHandled();
1344 dispatchSimulatedClick(evt);
1345 evt->setDefaultHandled();
1346 } else if (clickDefaultFormButton) {
1347 if (isSearchField())
1350 // Form may never have been present, or may have been destroyed by the blur event.
1352 form()->submitClick(evt);
1353 evt->setDefaultHandled();
1354 } else if (inputType() != SEARCH) {
1355 evt->setDefaultHandled();
1360 if (evt->isBeforeTextInsertedEvent()) {
1361 // Make sure that the text to be inserted will not violate the maxLength.
1362 int oldLen = numGraphemeClusters(value().impl());
1363 ASSERT(oldLen <= maxLength());
1364 int selectionLen = numGraphemeClusters(document()->frame()->selectionController()->toString().impl());
1365 ASSERT(oldLen >= selectionLen);
1366 int maxNewLen = maxLength() - (oldLen - selectionLen);
1368 // Truncate the inserted text to avoid violating the maxLength and other constraints.
1369 BeforeTextInsertedEvent* textEvent = static_cast<BeforeTextInsertedEvent*>(evt);
1370 textEvent->setText(constrainValue(textEvent->text(), maxNewLen));
1373 if (isTextField() && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == blurEvent || evt->type() == focusEvent))
1374 static_cast<RenderTextControl*>(renderer())->forwardEvent(evt);
1376 if (inputType() == RANGE && renderer()) {
1377 RenderSlider* slider = static_cast<RenderSlider*>(renderer());
1378 if (evt->isMouseEvent() && evt->type() == mousedownEvent) {
1379 MouseEvent* mEvt = static_cast<MouseEvent*>(evt);
1380 if (!slider->mouseEventIsInThumb(mEvt)) {
1381 slider->setValueForPosition(slider->positionForOffset(IntPoint(mEvt->offsetX(), mEvt->offsetY())));
1384 if (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent())
1385 slider->forwardEvent(evt);
1388 if (!evt->defaultHandled())
1389 HTMLGenericFormElement::defaultEventHandler(evt);
1392 bool HTMLInputElement::isURLAttribute(Attribute *attr) const
1394 return (attr->name() == srcAttr);
1397 String HTMLInputElement::defaultValue() const
1399 return getAttribute(valueAttr);
1402 void HTMLInputElement::setDefaultValue(const String &value)
1404 setAttribute(valueAttr, value);
1407 bool HTMLInputElement::defaultChecked() const
1409 return !getAttribute(checkedAttr).isNull();
1412 void HTMLInputElement::setDefaultChecked(bool defaultChecked)
1414 setAttribute(checkedAttr, defaultChecked ? "" : 0);
1417 String HTMLInputElement::accept() const
1419 return getAttribute(acceptAttr);
1422 void HTMLInputElement::setAccept(const String &value)
1424 setAttribute(acceptAttr, value);
1427 String HTMLInputElement::accessKey() const
1429 return getAttribute(accesskeyAttr);
1432 void HTMLInputElement::setAccessKey(const String &value)
1434 setAttribute(accesskeyAttr, value);
1437 String HTMLInputElement::align() const
1439 return getAttribute(alignAttr);
1442 void HTMLInputElement::setAlign(const String &value)
1444 setAttribute(alignAttr, value);
1447 String HTMLInputElement::alt() const
1449 return getAttribute(altAttr);
1452 void HTMLInputElement::setAlt(const String &value)
1454 setAttribute(altAttr, value);
1457 void HTMLInputElement::setMaxLength(int _maxLength)
1459 setAttribute(maxlengthAttr, String::number(_maxLength));
1462 void HTMLInputElement::setSize(unsigned _size)
1464 setAttribute(sizeAttr, String::number(_size));
1467 String HTMLInputElement::src() const
1469 return document()->completeURL(getAttribute(srcAttr));
1472 void HTMLInputElement::setSrc(const String &value)
1474 setAttribute(srcAttr, value);
1477 String HTMLInputElement::useMap() const
1479 return getAttribute(usemapAttr);
1482 void HTMLInputElement::setUseMap(const String &value)
1484 setAttribute(usemapAttr, value);
1487 String HTMLInputElement::constrainValue(const String& proposedValue) const
1489 return constrainValue(proposedValue, m_maxLen);
1492 void HTMLInputElement::recheckValue()
1494 String oldValue = value();
1495 String newValue = constrainValue(oldValue);
1496 if (newValue != oldValue)
1500 String HTMLInputElement::constrainValue(const String& proposedValue, int maxLen) const
1502 if (isTextField()) {
1503 StringImpl* s = proposedValue.impl();
1504 int newLen = numCharactersInGraphemeClusters(s, maxLen);
1505 for (int i = 0; i < newLen; ++i)
1506 if ((*s)[i] < ' ') {
1510 if (newLen < static_cast<int>(proposedValue.length()))
1511 return proposedValue.substring(0, newLen);
1513 return proposedValue;
1516 void HTMLInputElement::addSearchResult()
1518 ASSERT(isSearchField());
1520 static_cast<RenderTextControl*>(renderer())->addSearchResult();