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;
150 // Need to remove this from the form while it is still an HTMLInputElement,
151 // so can't wait for the base class's destructor to do it.
155 const AtomicString& HTMLInputElement::name() const
157 return m_name.isNull() ? emptyAtom : m_name;
160 static inline HTMLFormElement::CheckedRadioButtons& checkedRadioButtons(const HTMLInputElement *element)
162 if (HTMLFormElement* form = element->form())
163 return form->checkedRadioButtons();
165 return element->document()->checkedRadioButtons();
168 bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const
170 // If text fields can be focused, then they should always be keyboard focusable
172 return HTMLFormControlElementWithState::isFocusable();
174 // If the base class says we can't be focused, then we can stop now.
175 if (!HTMLFormControlElementWithState::isKeyboardFocusable(event))
178 if (inputType() == RADIO) {
179 // Unnamed radio buttons are never focusable (matches WinIE).
180 if (name().isEmpty())
183 // Never allow keyboard tabbing to leave you in the same radio group. Always
184 // skip any other elements in the group.
185 Node* currentFocusedNode = document()->focusedNode();
186 if (currentFocusedNode && currentFocusedNode->hasTagName(inputTag)) {
187 HTMLInputElement* focusedInput = static_cast<HTMLInputElement*>(currentFocusedNode);
188 if (focusedInput->inputType() == RADIO && focusedInput->form() == form() &&
189 focusedInput->name() == name())
193 // Allow keyboard focus if we're checked or if nothing in the group is checked.
194 return checked() || !checkedRadioButtons(this).checkedButtonForGroup(name());
200 bool HTMLInputElement::isMouseFocusable() const
203 return HTMLFormControlElementWithState::isFocusable();
204 return HTMLFormControlElementWithState::isMouseFocusable();
207 void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
210 if (!restorePreviousSelection || cachedSelStart == -1)
213 // Restore the cached selection.
214 setSelectionRange(cachedSelStart, cachedSelEnd);
216 if (document() && document()->frame())
217 document()->frame()->revealSelection();
219 HTMLFormControlElementWithState::updateFocusAppearance(restorePreviousSelection);
222 void HTMLInputElement::aboutToUnload()
224 if (isTextField() && focused() && document()->frame())
225 document()->frame()->textFieldDidEndEditing(this);
228 bool HTMLInputElement::shouldUseInputMethod() const
230 return m_type == TEXT || m_type == SEARCH || m_type == ISINDEX;
233 void HTMLInputElement::dispatchFocusEvent()
236 setAutofilled(false);
237 if (inputType() == PASSWORD && document()->frame())
238 document()->setUseSecureKeyboardEntryWhenActive(true);
240 HTMLFormControlElementWithState::dispatchFocusEvent();
243 void HTMLInputElement::dispatchBlurEvent()
245 if (isTextField() && document()->frame()) {
246 if (inputType() == PASSWORD)
247 document()->setUseSecureKeyboardEntryWhenActive(false);
248 document()->frame()->textFieldDidEndEditing(this);
250 HTMLFormControlElementWithState::dispatchBlurEvent();
253 void HTMLInputElement::setType(const String& t)
257 removeAttribute(typeAttr, exccode);
259 setAttribute(typeAttr, t);
262 void HTMLInputElement::setInputType(const String& t)
266 if (equalIgnoringCase(t, "password"))
268 else if (equalIgnoringCase(t, "checkbox"))
270 else if (equalIgnoringCase(t, "radio"))
272 else if (equalIgnoringCase(t, "submit"))
274 else if (equalIgnoringCase(t, "reset"))
276 else if (equalIgnoringCase(t, "file"))
278 else if (equalIgnoringCase(t, "hidden"))
280 else if (equalIgnoringCase(t, "image"))
282 else if (equalIgnoringCase(t, "button"))
284 else if (equalIgnoringCase(t, "khtml_isindex"))
286 else if (equalIgnoringCase(t, "search"))
288 else if (equalIgnoringCase(t, "range"))
293 // IMPORTANT: Don't allow the type to be changed to FILE after the first
294 // type change, otherwise a JavaScript programmer would be able to set a text
295 // field's value to something like /etc/passwd and then change it to a file field.
296 if (inputType() != newType) {
297 if (newType == FILE && m_haveType)
298 // Set the attribute back to the old value.
299 // Useful in case we were called from inside parseMappedAttribute.
300 setAttribute(typeAttr, type());
302 checkedRadioButtons(this).removeButton(this);
304 bool wasAttached = m_attached;
308 bool didStoreValue = storesValueSeparateFromAttribute();
309 bool wasPasswordField = inputType() == PASSWORD;
310 bool didRespectHeightAndWidth = respectHeightAndWidthAttrs();
312 bool willStoreValue = storesValueSeparateFromAttribute();
313 bool isPasswordField = inputType() == PASSWORD;
314 bool willRespectHeightAndWidth = respectHeightAndWidthAttrs();
316 if (didStoreValue && !willStoreValue && !m_value.isNull()) {
317 setAttribute(valueAttr, m_value);
320 if (!didStoreValue && willStoreValue)
321 m_value = constrainValue(getAttribute(valueAttr));
325 if (wasPasswordField && !isPasswordField)
326 document()->unregisterForCacheCallbacks(this);
327 else if (!wasPasswordField && isPasswordField)
328 document()->registerForCacheCallbacks(this);
330 if (didRespectHeightAndWidth != willRespectHeightAndWidth) {
331 NamedMappedAttrMap* map = mappedAttributes();
332 if (MappedAttribute* height = map->getAttributeItem(heightAttr))
333 attributeChanged(height, false);
334 if (MappedAttribute* width = map->getAttributeItem(widthAttr))
335 attributeChanged(width, false);
336 if (MappedAttribute* align = map->getAttributeItem(alignAttr))
337 attributeChanged(align, false);
343 checkedRadioButtons(this).addButton(this);
348 if (inputType() != IMAGE && m_imageLoader) {
349 delete m_imageLoader;
354 const AtomicString& HTMLInputElement::type() const
356 // needs to be lowercase according to DOM spec
357 switch (inputType()) {
359 static const AtomicString button("button");
363 static const AtomicString checkbox("checkbox");
367 static const AtomicString file("file");
371 static const AtomicString hidden("hidden");
375 static const AtomicString image("image");
381 static const AtomicString password("password");
385 static const AtomicString radio("radio");
389 static const AtomicString range("range");
393 static const AtomicString reset("reset");
397 static const AtomicString search("search");
401 static const AtomicString submit("submit");
405 static const AtomicString text("text");
412 bool HTMLInputElement::saveState(String& result) const
414 switch (inputType()) {
429 result = checked() ? "on" : "off";
434 ASSERT_NOT_REACHED();
438 void HTMLInputElement::restoreState(const String& state)
440 ASSERT(inputType() != PASSWORD); // should never save/restore password fields
441 switch (inputType()) {
456 setChecked(state == "on");
463 bool HTMLInputElement::canStartSelection() const
467 return HTMLFormControlElementWithState::canStartSelection();
470 bool HTMLInputElement::canHaveSelection() const
472 return isTextField();
475 int HTMLInputElement::selectionStart() const
479 if (document()->focusedNode() != this && cachedSelStart != -1)
480 return cachedSelStart;
483 return static_cast<RenderTextControl*>(renderer())->selectionStart();
486 int HTMLInputElement::selectionEnd() const
490 if (document()->focusedNode() != this && cachedSelEnd != -1)
494 return static_cast<RenderTextControl*>(renderer())->selectionEnd();
497 void HTMLInputElement::setSelectionStart(int start)
503 static_cast<RenderTextControl*>(renderer())->setSelectionStart(start);
506 void HTMLInputElement::setSelectionEnd(int end)
512 static_cast<RenderTextControl*>(renderer())->setSelectionEnd(end);
515 void HTMLInputElement::select()
521 static_cast<RenderTextControl*>(renderer())->select();
524 void HTMLInputElement::setSelectionRange(int start, int end)
530 static_cast<RenderTextControl*>(renderer())->setSelectionRange(start, end);
533 void HTMLInputElement::accessKeyAction(bool sendToAnyElement)
535 switch (inputType()) {
545 // send the mouse button events iff the caller specified sendToAnyElement
546 dispatchSimulatedClick(0, sendToAnyElement);
549 // a no-op for this type
555 // should never restore previous selection here
561 bool HTMLInputElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
563 if (((attrName == heightAttr || attrName == widthAttr) && respectHeightAndWidthAttrs()) ||
564 attrName == vspaceAttr ||
565 attrName == hspaceAttr) {
570 if (attrName == alignAttr) {
571 if (inputType() == IMAGE) {
572 // Share with <img> since the alignment behavior is the same.
578 return HTMLElement::mapToEntry(attrName, result);
581 void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr)
583 if (attr->name() == nameAttr) {
584 checkedRadioButtons(this).removeButton(this);
585 m_name = attr->value();
586 checkedRadioButtons(this).addButton(this);
587 } else if (attr->name() == autocompleteAttr) {
588 m_autocomplete = !equalIgnoringCase(attr->value(), "off");
589 } else if (attr->name() == typeAttr) {
590 setInputType(attr->value());
591 } else if (attr->name() == valueAttr) {
592 // We only need to setChanged if the form is looking at the default value right now.
593 if (m_value.isNull())
595 setValueMatchesRenderer(false);
596 } else if (attr->name() == checkedAttr) {
597 m_defaultChecked = !attr->isNull();
598 if (m_useDefaultChecked) {
599 setChecked(m_defaultChecked);
600 m_useDefaultChecked = true;
602 } else if (attr->name() == maxlengthAttr) {
603 int oldMaxLen = m_maxLen;
604 m_maxLen = !attr->isNull() ? attr->value().toInt() : cMaxLen;
605 if (m_maxLen <= 0 || m_maxLen > cMaxLen)
607 if (oldMaxLen != m_maxLen)
610 } else if (attr->name() == sizeAttr) {
611 m_size = !attr->isNull() ? attr->value().toInt() : 20;
612 } else if (attr->name() == altAttr) {
613 if (renderer() && inputType() == IMAGE)
614 static_cast<RenderImage*>(renderer())->updateAltText();
615 } else if (attr->name() == srcAttr) {
616 if (renderer() && inputType() == IMAGE) {
618 m_imageLoader = new HTMLImageLoader(this);
619 m_imageLoader->updateFromElement();
621 } else if (attr->name() == usemapAttr ||
622 attr->name() == accesskeyAttr) {
623 // FIXME: ignore for the moment
624 } else if (attr->name() == vspaceAttr) {
625 addCSSLength(attr, CSS_PROP_MARGIN_TOP, attr->value());
626 addCSSLength(attr, CSS_PROP_MARGIN_BOTTOM, attr->value());
627 } else if (attr->name() == hspaceAttr) {
628 addCSSLength(attr, CSS_PROP_MARGIN_LEFT, attr->value());
629 addCSSLength(attr, CSS_PROP_MARGIN_RIGHT, attr->value());
630 } else if (attr->name() == alignAttr) {
631 if (inputType() == IMAGE)
632 addHTMLAlignment(attr);
633 } else if (attr->name() == widthAttr) {
634 if (respectHeightAndWidthAttrs())
635 addCSSLength(attr, CSS_PROP_WIDTH, attr->value());
636 } else if (attr->name() == heightAttr) {
637 if (respectHeightAndWidthAttrs())
638 addCSSLength(attr, CSS_PROP_HEIGHT, attr->value());
639 } else if (attr->name() == onfocusAttr) {
640 setHTMLEventListener(focusEvent, attr);
641 } else if (attr->name() == onblurAttr) {
642 setHTMLEventListener(blurEvent, attr);
643 } else if (attr->name() == onselectAttr) {
644 setHTMLEventListener(selectEvent, attr);
645 } else if (attr->name() == onchangeAttr) {
646 setHTMLEventListener(changeEvent, attr);
647 } else if (attr->name() == oninputAttr) {
648 setHTMLEventListener(inputEvent, attr);
650 // Search field and slider attributes all just cause updateFromElement to be called through style
652 else if (attr->name() == onsearchAttr) {
653 setHTMLEventListener(searchEvent, attr);
654 } else if (attr->name() == resultsAttr) {
655 int oldResults = m_maxResults;
656 m_maxResults = !attr->isNull() ? min(attr->value().toInt(), maxSavedResults) : -1;
657 // FIXME: Detaching just for maxResults change is not ideal. We should figure out the right
658 // time to relayout for this change.
659 if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0) && attached()) {
664 } else if (attr->name() == autosaveAttr ||
665 attr->name() == incrementalAttr ||
666 attr->name() == placeholderAttr ||
667 attr->name() == minAttr ||
668 attr->name() == maxAttr ||
669 attr->name() == precisionAttr) {
672 HTMLFormControlElementWithState::parseMappedAttribute(attr);
675 bool HTMLInputElement::rendererIsNeeded(RenderStyle *style)
677 switch (inputType()) {
690 return HTMLFormControlElementWithState::rendererIsNeeded(style);
698 RenderObject *HTMLInputElement::createRenderer(RenderArena *arena, RenderStyle *style)
700 switch (inputType()) {
704 return new (arena) RenderButton(this);
707 return RenderObject::createObject(this, style);
709 return new (arena) RenderFileUploadControl(this);
713 return new (arena) RenderImage(this);
715 return new (arena) RenderSlider(this);
720 return new (arena) RenderTextControl(this, false);
726 void HTMLInputElement::attach()
730 setInputType(getAttribute(typeAttr));
734 HTMLFormControlElementWithState::attach();
736 if (inputType() == IMAGE) {
738 m_imageLoader = new HTMLImageLoader(this);
739 m_imageLoader->updateFromElement();
741 RenderImage* imageObj = static_cast<RenderImage*>(renderer());
742 imageObj->setCachedImage(m_imageLoader->image());
744 // If we have no image at all because we have no src attribute, set
745 // image height and width for the alt text instead.
746 if (!m_imageLoader->image() && !imageObj->cachedImage())
747 imageObj->setImageSizeForAltText();
752 void HTMLInputElement::detach()
754 HTMLFormControlElementWithState::detach();
755 setValueMatchesRenderer(false);
758 String HTMLInputElement::altText() const
760 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
761 // also heavily discussed by Hixie on bugzilla
762 // note this is intentionally different to HTMLImageElement::altText()
763 String alt = getAttribute(altAttr);
764 // fall back to title attribute
766 alt = getAttribute(titleAttr);
768 alt = getAttribute(valueAttr);
770 alt = inputElementAltText();
774 bool HTMLInputElement::isSuccessfulSubmitButton() const
776 // HTML spec says that buttons must have names to be considered successful.
777 // However, other browsers do not impose this constraint. So we do likewise.
778 return !disabled() && (inputType() == IMAGE || inputType() == SUBMIT);
781 bool HTMLInputElement::isActivatedSubmit() const
783 return m_activeSubmit;
786 void HTMLInputElement::setActivatedSubmit(bool flag)
788 m_activeSubmit = flag;
791 bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart)
793 // image generates its own names, but for other types there is no form data unless there's a name
794 if (name().isEmpty() && inputType() != IMAGE)
797 switch (inputType()) {
805 encoding.appendData(name(), value());
811 encoding.appendData(name(), value());
818 // these types of buttons are never successful
822 if (m_activeSubmit) {
823 encoding.appendData(name().isEmpty() ? "x" : (name() + ".x"), xPos);
824 encoding.appendData(name().isEmpty() ? "y" : (name() + ".y"), yPos);
825 if (!name().isEmpty() && !value().isEmpty())
826 encoding.appendData(name(), value());
832 if (m_activeSubmit) {
833 String enc_str = valueWithDefault();
834 encoding.appendData(name(), enc_str);
840 // Can't submit file on GET.
844 // If no filename at all is entered, return successful but empty.
845 // Null would be more logical, but Netscape posts an empty file. Argh.
846 if (value().isEmpty()) {
847 encoding.appendData(name(), String(""));
851 encoding.appendFile(name(), value());
857 void HTMLInputElement::reset()
859 if (storesValueSeparateFromAttribute())
862 setChecked(m_defaultChecked);
863 m_useDefaultChecked = true;
866 void HTMLInputElement::setChecked(bool nowChecked, bool sendChangeEvent)
868 if (checked() == nowChecked)
871 m_useDefaultChecked = false;
872 m_checked = nowChecked;
875 checkedRadioButtons(this).addButton(this);
877 if (renderer() && renderer()->style()->hasAppearance())
878 theme()->stateChanged(renderer(), CheckedState);
880 // Only send a change event for items in the document (avoid firing during
881 // parsing) and don't send a change event for a radio button that's getting
882 // unchecked to match other browsers. DOM is not a useful standard for this
883 // because it says only to fire change events at "lose focus" time, which is
884 // definitely wrong in practice for these types of elements.
885 if (sendChangeEvent && inDocument() && (inputType() != RADIO || nowChecked))
889 void HTMLInputElement::setIndeterminate(bool _indeterminate)
891 // Only checkboxes honor indeterminate.
892 if (inputType() != CHECKBOX || indeterminate() == _indeterminate)
895 m_indeterminate = _indeterminate;
899 if (renderer() && renderer()->style()->hasAppearance())
900 theme()->stateChanged(renderer(), CheckedState);
903 void HTMLInputElement::copyNonAttributeProperties(const Element *source)
905 const HTMLInputElement *sourceElem = static_cast<const HTMLInputElement *>(source);
907 m_value = sourceElem->m_value;
908 m_checked = sourceElem->m_checked;
909 m_indeterminate = sourceElem->m_indeterminate;
911 HTMLFormControlElementWithState::copyNonAttributeProperties(source);
914 String HTMLInputElement::value() const
916 String value = m_value;
918 // It's important *not* to fall back to the value attribute for file inputs,
919 // because that would allow a malicious web page to upload files by setting the
920 // value attribute in markup.
921 if (value.isNull() && inputType() != FILE)
922 value = constrainValue(getAttribute(valueAttr));
924 // If no attribute exists, then just use "on" or "" based off the checked() state of the control.
925 if (value.isNull() && (inputType() == CHECKBOX || inputType() == RADIO))
926 return checked() ? "on" : "";
931 String HTMLInputElement::valueWithDefault() const
935 switch (inputType()) {
949 v = resetButtonDefaultLabel();
952 v = submitButtonDefaultLabel();
959 void HTMLInputElement::setValue(const String& value)
961 // For security reasons, we don't allow setting the filename, but we do allow clearing it.
962 if (inputType() == FILE && !value.isEmpty())
965 setValueMatchesRenderer(false);
966 if (storesValueSeparateFromAttribute()) {
967 m_value = constrainValue(value);
968 if (isTextField() && inDocument())
969 document()->updateRendering();
971 renderer()->updateFromElement();
974 setAttribute(valueAttr, constrainValue(value));
977 unsigned max = m_value.length();
978 if (document()->focusedNode() == this)
979 setSelectionRange(max, max);
981 cachedSelStart = max;
987 void HTMLInputElement::setValueFromRenderer(const String& value)
989 // Renderer and our event handler are responsible for constraining values.
990 ASSERT(value == constrainValue(value) || constrainValue(value).isEmpty());
992 // Workaround for bug where trailing \n is included in the result of textContent.
993 // The assert macro above may also be simplified to: value == constrainValue(value)
994 // http://bugs.webkit.org/show_bug.cgi?id=9661
1000 setValueMatchesRenderer();
1002 // Fire the "input" DOM event.
1003 dispatchHTMLEvent(inputEvent, true, false);
1006 bool HTMLInputElement::storesValueSeparateFromAttribute() const
1008 switch (inputType()) {
1028 void* HTMLInputElement::preDispatchEventHandler(Event *evt)
1030 // preventDefault or "return false" are used to reverse the automatic checking/selection we do here.
1031 // This result gives us enough info to perform the "undo" in postDispatch of the action we take here.
1033 if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
1034 && evt->type() == clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1035 if (inputType() == CHECKBOX) {
1036 // As a way to store the state, we return 0 if we were unchecked, 1 if we were checked, and 2 for
1038 if (indeterminate()) {
1039 result = (void*)0x2;
1040 setIndeterminate(false);
1043 result = (void*)0x1;
1044 setChecked(!checked(), true);
1047 // For radio buttons, store the current selected radio object.
1048 if (name().isEmpty() || checked())
1049 return 0; // Match WinIE and don't allow unnamed radio buttons to be checked.
1050 // Checked buttons just stay checked.
1051 // FIXME: Need to learn to work without a form.
1053 // We really want radio groups to end up in sane states, i.e., to have something checked.
1054 // Therefore if nothing is currently selected, we won't allow this action to be "undone", since
1055 // we want some object in the radio group to actually get selected.
1056 HTMLInputElement* currRadio = checkedRadioButtons(this).checkedButtonForGroup(name());
1058 // We have a radio button selected that is not us. Cache it in our result field and ref it so
1059 // that it can't be destroyed.
1063 setChecked(true, true);
1069 void HTMLInputElement::postDispatchEventHandler(Event *evt, void* data)
1071 if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
1072 && evt->type() == clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1073 if (inputType() == CHECKBOX) {
1074 // Reverse the checking we did in preDispatch.
1075 if (evt->defaultPrevented() || evt->defaultHandled()) {
1076 if (data == (void*)0x2)
1077 setIndeterminate(true);
1082 HTMLInputElement* input = static_cast<HTMLInputElement*>(data);
1083 if (evt->defaultPrevented() || evt->defaultHandled()) {
1084 // Restore the original selected radio button if possible.
1085 // Make sure it is still a radio button and only do the restoration if it still
1086 // belongs to our group.
1088 // Match WinIE and don't allow unnamed radio buttons to be checked.
1089 if (input->form() == form() && input->inputType() == RADIO && !name().isEmpty() && input->name() == name()) {
1090 // Ok, the old radio button is still in our form and in our group and is still a
1091 // radio button, so it's safe to restore selection to it.
1092 input->setChecked(true);
1098 // Left clicks on radio buttons and check boxes already performed default actions in preDispatchEventHandler().
1099 evt->setDefaultHandled();
1103 void HTMLInputElement::defaultEventHandler(Event* evt)
1105 bool clickDefaultFormButton = false;
1107 if (isTextField() && evt->type() == textInputEvent && evt->isTextEvent() && static_cast<TextEvent*>(evt)->data() == "\n")
1108 clickDefaultFormButton = true;
1110 if (inputType() == IMAGE && evt->isMouseEvent() && evt->type() == clickEvent) {
1111 // record the mouse position for when we get the DOMActivate event
1112 MouseEvent* me = static_cast<MouseEvent*>(evt);
1113 // FIXME: We could just call offsetX() and offsetY() on the event,
1114 // but that's currently broken, so for now do the computation here.
1115 if (me->isSimulated() || !renderer()) {
1119 int offsetX, offsetY;
1120 renderer()->absolutePosition(offsetX, offsetY);
1121 xPos = me->pageX() - offsetX;
1122 // FIXME: Why is yPos a short?
1123 yPos = me->pageY() - offsetY;
1127 // Before calling the base class defaultEventHandler, which will call handleKeypress, call doTextFieldCommandFromEvent.
1128 if (isTextField() && evt->type() == keypressEvent && evt->isKeyboardEvent() && focused() && document()->frame()
1129 && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) {
1130 evt->setDefaultHandled();
1134 if (inputType() == RADIO && evt->isMouseEvent()
1135 && evt->type() == clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1136 evt->setDefaultHandled();
1140 // Let the key handling done in EventTargetNode take precedence over the event handling here for editable text fields
1141 if (!clickDefaultFormButton) {
1142 HTMLFormControlElementWithState::defaultEventHandler(evt);
1143 if (evt->defaultHandled())
1147 // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means
1148 // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks
1149 // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element
1150 // must dispatch a DOMActivate event - a click event will not do the job.
1151 if (evt->type() == DOMActivateEvent && !disabled()) {
1152 if (inputType() == IMAGE || inputType() == SUBMIT || inputType() == RESET) {
1155 if (inputType() == RESET)
1158 m_activeSubmit = true;
1159 // FIXME: Would be cleaner to get xPos and yPos out of the underlying mouse
1160 // event (if any) here instead of relying on the variables set above when
1161 // processing the click event. Even better, appendFormData could pass the
1162 // event in, and then we could get rid of xPos and yPos altogether!
1163 if (!form()->prepareSubmit(evt)) {
1167 m_activeSubmit = false;
1169 } else if (inputType() == FILE && renderer())
1170 static_cast<RenderFileUploadControl*>(renderer())->click();
1173 // Use key press event here since sending simulated mouse events
1174 // on key down blocks the proper sending of the key press event.
1175 if (evt->type() == keypressEvent && evt->isKeyboardEvent()) {
1176 bool clickElement = false;
1178 String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
1180 if (key == "U+0020") {
1181 switch (inputType()) {
1188 // Simulate mouse click for spacebar for these types of elements.
1189 // The AppKit already does this for some, but not all, of them.
1190 clickElement = true;
1193 // If an unselected radio is tabbed into (because the entire group has nothing
1194 // checked, or because of some explicit .focus() call), then allow space to check it.
1196 clickElement = true;
1208 if (key == "Enter") {
1209 switch (inputType()) {
1218 // Simulate mouse click on the default form button for enter for these types of elements.
1219 clickDefaultFormButton = true;
1225 // Simulate mouse click for enter for these types of elements.
1226 clickElement = true;
1229 break; // Don't do anything for enter on a radio button.
1233 if (inputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) {
1234 // Left and up mean "previous radio button".
1235 // Right and down mean "next radio button".
1236 // Tested in WinIE, and even for RTL, left still means previous radio button (and so moves
1237 // to the right). Seems strange, but we'll match it.
1238 bool forward = (key == "Down" || key == "Right");
1240 // We can only stay within the form's children if the form hasn't been demoted to a leaf because
1241 // of malformed HTML.
1243 while ((n = (forward ? n->traverseNextNode() : n->traversePreviousNode()))) {
1244 // Once we encounter a form element, we know we're through.
1245 if (n->hasTagName(formTag))
1248 // Look for more radio buttons.
1249 if (n->hasTagName(inputTag)) {
1250 HTMLInputElement* elt = static_cast<HTMLInputElement*>(n);
1251 if (elt->form() != form())
1253 if (n->hasTagName(inputTag)) {
1254 // Match WinIE and don't allow unnamed radio buttons to be checked.
1255 HTMLInputElement* inputElt = static_cast<HTMLInputElement*>(n);
1256 if (inputElt->inputType() == RADIO && !name().isEmpty() && inputElt->name() == name() && inputElt->isFocusable()) {
1257 inputElt->setChecked(true);
1258 document()->setFocusedNode(inputElt);
1259 inputElt->dispatchSimulatedClick(evt, false, false);
1260 evt->setDefaultHandled();
1269 dispatchSimulatedClick(evt);
1270 evt->setDefaultHandled();
1275 if (clickDefaultFormButton) {
1276 if (isSearchField()) {
1280 // Fire onChange for text fields.
1281 RenderObject* r = renderer();
1282 if (r && r->isTextField() && r->isEdited()) {
1284 // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it.
1287 r->setEdited(false);
1289 // Form may never have been present, or may have been destroyed by the change event.
1291 form()->submitClick(evt);
1292 evt->setDefaultHandled();
1296 if (evt->isBeforeTextInsertedEvent()) {
1297 // Make sure that the text to be inserted will not violate the maxLength.
1298 int oldLen = numGraphemeClusters(value().impl());
1299 ASSERT(oldLen <= maxLength());
1300 int selectionLen = numGraphemeClusters(document()->frame()->selectionController()->toString().impl());
1301 ASSERT(oldLen >= selectionLen);
1302 int maxNewLen = maxLength() - (oldLen - selectionLen);
1304 // Truncate the inserted text to avoid violating the maxLength and other constraints.
1305 BeforeTextInsertedEvent* textEvent = static_cast<BeforeTextInsertedEvent*>(evt);
1306 textEvent->setText(constrainValue(textEvent->text(), maxNewLen));
1309 if (isTextField() && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == blurEvent || evt->type() == focusEvent))
1310 static_cast<RenderTextControl*>(renderer())->forwardEvent(evt);
1312 if (inputType() == RANGE && renderer()) {
1313 RenderSlider* slider = static_cast<RenderSlider*>(renderer());
1314 if (evt->isMouseEvent() && evt->type() == mousedownEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1315 MouseEvent* mEvt = static_cast<MouseEvent*>(evt);
1316 if (!slider->mouseEventIsInThumb(mEvt)) {
1317 slider->setValueForPosition(slider->positionForOffset(IntPoint(mEvt->offsetX(), mEvt->offsetY())));
1320 if (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent())
1321 slider->forwardEvent(evt);
1325 bool HTMLInputElement::isURLAttribute(Attribute *attr) const
1327 return (attr->name() == srcAttr);
1330 String HTMLInputElement::defaultValue() const
1332 return getAttribute(valueAttr);
1335 void HTMLInputElement::setDefaultValue(const String &value)
1337 setAttribute(valueAttr, value);
1340 bool HTMLInputElement::defaultChecked() const
1342 return !getAttribute(checkedAttr).isNull();
1345 void HTMLInputElement::setDefaultChecked(bool defaultChecked)
1347 setAttribute(checkedAttr, defaultChecked ? "" : 0);
1350 String HTMLInputElement::accept() const
1352 return getAttribute(acceptAttr);
1355 void HTMLInputElement::setAccept(const String &value)
1357 setAttribute(acceptAttr, value);
1360 String HTMLInputElement::accessKey() const
1362 return getAttribute(accesskeyAttr);
1365 void HTMLInputElement::setAccessKey(const String &value)
1367 setAttribute(accesskeyAttr, value);
1370 String HTMLInputElement::align() const
1372 return getAttribute(alignAttr);
1375 void HTMLInputElement::setAlign(const String &value)
1377 setAttribute(alignAttr, value);
1380 String HTMLInputElement::alt() const
1382 return getAttribute(altAttr);
1385 void HTMLInputElement::setAlt(const String &value)
1387 setAttribute(altAttr, value);
1390 void HTMLInputElement::setMaxLength(int _maxLength)
1392 setAttribute(maxlengthAttr, String::number(_maxLength));
1395 void HTMLInputElement::setSize(unsigned _size)
1397 setAttribute(sizeAttr, String::number(_size));
1400 String HTMLInputElement::src() const
1402 return document()->completeURL(getAttribute(srcAttr));
1405 void HTMLInputElement::setSrc(const String &value)
1407 setAttribute(srcAttr, value);
1410 String HTMLInputElement::useMap() const
1412 return getAttribute(usemapAttr);
1415 void HTMLInputElement::setUseMap(const String &value)
1417 setAttribute(usemapAttr, value);
1420 String HTMLInputElement::constrainValue(const String& proposedValue) const
1422 return constrainValue(proposedValue, m_maxLen);
1425 void HTMLInputElement::recheckValue()
1427 String oldValue = value();
1428 String newValue = constrainValue(oldValue);
1429 if (newValue != oldValue)
1433 String HTMLInputElement::constrainValue(const String& proposedValue, int maxLen) const
1435 if (isTextField()) {
1436 StringImpl* s = proposedValue.impl();
1437 int newLen = numCharactersInGraphemeClusters(s, maxLen);
1438 for (int i = 0; i < newLen; ++i) {
1439 const UChar current = (*s)[i];
1440 if (current < ' ' && current != '\t') {
1445 if (newLen < static_cast<int>(proposedValue.length()))
1446 return proposedValue.substring(0, newLen);
1448 return proposedValue;
1451 void HTMLInputElement::addSearchResult()
1453 ASSERT(isSearchField());
1455 static_cast<RenderTextControl*>(renderer())->addSearchResult();
1458 void HTMLInputElement::onSearch()
1460 ASSERT(isSearchField());
1462 static_cast<RenderTextControl*>(renderer())->stopSearchEventTimer();
1463 dispatchHTMLEvent(searchEvent, true, false);
1466 Selection HTMLInputElement::selection() const
1468 if (!renderer() || !isTextField() || cachedSelStart == -1 || cachedSelEnd == -1)
1470 return static_cast<RenderTextControl*>(renderer())->selection(cachedSelStart, cachedSelEnd);
1473 void HTMLInputElement::didRestoreFromCache()
1475 ASSERT(inputType() == PASSWORD);
1479 void HTMLInputElement::willMoveToNewOwnerDocument()
1481 if (inputType() == PASSWORD)
1482 document()->unregisterForCacheCallbacks(this);
1484 document()->checkedRadioButtons().removeButton(this);
1486 HTMLFormControlElementWithState::willMoveToNewOwnerDocument();
1489 void HTMLInputElement::didMoveToNewOwnerDocument()
1491 if (inputType() == PASSWORD)
1492 document()->registerForCacheCallbacks(this);
1494 HTMLFormControlElementWithState::didMoveToNewOwnerDocument();