• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
6  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7  * Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
8  *
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.
13  *
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.
18  *
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.
23  *
24  */
25 
26 #include "config.h"
27 #include "HTMLInputElement.h"
28 
29 #include "AXObjectCache.h"
30 #include "CSSPropertyNames.h"
31 #include "ChromeClient.h"
32 #include "Document.h"
33 #include "Editor.h"
34 #include "Event.h"
35 #include "EventHandler.h"
36 #include "EventNames.h"
37 #include "File.h"
38 #include "FileList.h"
39 #include "FocusController.h"
40 #include "FormDataList.h"
41 #include "Frame.h"
42 #include "HTMLFormElement.h"
43 #include "HTMLImageLoader.h"
44 #include "HTMLNames.h"
45 #include "ScriptEventListener.h"
46 #include "KeyboardEvent.h"
47 #include "LocalizedStrings.h"
48 #include "MappedAttribute.h"
49 #include "MouseEvent.h"
50 #include "Page.h"
51 #include "RegularExpression.h"
52 #include "RenderButton.h"
53 #include "RenderFileUploadControl.h"
54 #include "RenderImage.h"
55 #include "RenderSlider.h"
56 #include "RenderText.h"
57 #include "RenderTextControlSingleLine.h"
58 #include "RenderTheme.h"
59 #include "TextEvent.h"
60 #ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS
61 #include "WebViewCore.h"
62 #endif
63 #include <wtf/StdLibExtras.h>
64 
65 using namespace std;
66 
67 namespace WebCore {
68 
69 using namespace HTMLNames;
70 
71 const int maxSavedResults = 256;
72 
HTMLInputElement(const QualifiedName & tagName,Document * doc,HTMLFormElement * f)73 HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f)
74     : HTMLFormControlElementWithState(tagName, doc, f)
75     , m_xPos(0)
76     , m_yPos(0)
77     , m_maxResults(-1)
78     , m_type(TEXT)
79     , m_checked(false)
80     , m_defaultChecked(false)
81     , m_useDefaultChecked(true)
82     , m_indeterminate(false)
83     , m_haveType(false)
84     , m_activeSubmit(false)
85     , m_autocomplete(Uninitialized)
86     , m_autofilled(false)
87     , m_inited(false)
88 {
89     ASSERT(hasTagName(inputTag) || hasTagName(isindexTag));
90 }
91 
~HTMLInputElement()92 HTMLInputElement::~HTMLInputElement()
93 {
94     if (needsActivationCallback())
95         document()->unregisterForDocumentActivationCallbacks(this);
96 
97     document()->checkedRadioButtons().removeButton(this);
98 
99     // Need to remove this from the form while it is still an HTMLInputElement,
100     // so can't wait for the base class's destructor to do it.
101     removeFromForm();
102 }
103 
formControlName() const104 const AtomicString& HTMLInputElement::formControlName() const
105 {
106     return m_data.name();
107 }
108 
autoComplete() const109 bool HTMLInputElement::autoComplete() const
110 {
111     if (m_autocomplete != Uninitialized)
112         return m_autocomplete == On;
113 
114     // Assuming we're still in a Form, respect the Form's setting
115     if (HTMLFormElement* form = this->form())
116         return form->autoComplete();
117 
118     // The default is true
119     return true;
120 }
121 
valueMissing() const122 bool HTMLInputElement::valueMissing() const
123 {
124     if (!isRequiredFormControl() || readOnly() || disabled())
125         return false;
126 
127     switch (inputType()) {
128         case TEXT:
129         case SEARCH:
130         case URL:
131         case TELEPHONE:
132         case EMAIL:
133         case PASSWORD:
134         case NUMBER:
135         case FILE:
136             return value().isEmpty();
137         case CHECKBOX:
138             return !checked();
139         case RADIO:
140             return !document()->checkedRadioButtons().checkedButtonForGroup(name());
141         case HIDDEN:
142         case RANGE:
143         case SUBMIT:
144         case IMAGE:
145         case RESET:
146         case BUTTON:
147         case ISINDEX:
148             break;
149     }
150 
151     ASSERT_NOT_REACHED();
152     return false;
153 }
154 
patternMismatch() const155 bool HTMLInputElement::patternMismatch() const
156 {
157     switch (inputType()) {
158         case ISINDEX:
159         case CHECKBOX:
160         case RADIO:
161         case SUBMIT:
162         case RESET:
163         case FILE:
164         case HIDDEN:
165         case IMAGE:
166         case BUTTON:
167         case RANGE:
168         case NUMBER:
169             return false;
170         case TEXT:
171         case SEARCH:
172         case URL:
173         case TELEPHONE:
174         case EMAIL:
175         case PASSWORD:
176             const AtomicString& pattern = getAttribute(patternAttr);
177             String value = this->value();
178 
179             // Empty values can't be mismatched
180             if (pattern.isEmpty() || value.isEmpty())
181                 return false;
182 
183             RegularExpression patternRegExp(pattern, TextCaseSensitive);
184             int matchLength = 0;
185             int valueLength = value.length();
186             int matchOffset = patternRegExp.match(value, 0, &matchLength);
187 
188             return matchOffset != 0 || matchLength != valueLength;
189     }
190 
191     ASSERT_NOT_REACHED();
192     return false;
193 }
194 
checkedRadioButtons(const HTMLInputElement * element)195 static inline CheckedRadioButtons& checkedRadioButtons(const HTMLInputElement *element)
196 {
197     if (HTMLFormElement* form = element->form())
198         return form->checkedRadioButtons();
199 
200     return element->document()->checkedRadioButtons();
201 }
202 
isKeyboardFocusable(KeyboardEvent * event) const203 bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const
204 {
205     // If text fields can be focused, then they should always be keyboard focusable
206     if (isTextField())
207         return HTMLFormControlElementWithState::isFocusable();
208 
209     // If the base class says we can't be focused, then we can stop now.
210     if (!HTMLFormControlElementWithState::isKeyboardFocusable(event))
211         return false;
212 
213     if (inputType() == RADIO) {
214 
215         // Never allow keyboard tabbing to leave you in the same radio group.  Always
216         // skip any other elements in the group.
217         Node* currentFocusedNode = document()->focusedNode();
218         if (currentFocusedNode && currentFocusedNode->hasTagName(inputTag)) {
219             HTMLInputElement* focusedInput = static_cast<HTMLInputElement*>(currentFocusedNode);
220             if (focusedInput->inputType() == RADIO && focusedInput->form() == form() &&
221                 focusedInput->name() == name())
222                 return false;
223         }
224 
225         // Allow keyboard focus if we're checked or if nothing in the group is checked.
226         return checked() || !checkedRadioButtons(this).checkedButtonForGroup(name());
227     }
228 
229     return true;
230 }
231 
isMouseFocusable() const232 bool HTMLInputElement::isMouseFocusable() const
233 {
234     if (isTextField())
235         return HTMLFormControlElementWithState::isFocusable();
236     return HTMLFormControlElementWithState::isMouseFocusable();
237 }
238 
updateFocusAppearance(bool restorePreviousSelection)239 void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
240 {
241     if (isTextField())
242         InputElement::updateFocusAppearance(m_data, this, this, restorePreviousSelection);
243     else
244         HTMLFormControlElementWithState::updateFocusAppearance(restorePreviousSelection);
245 }
246 
aboutToUnload()247 void HTMLInputElement::aboutToUnload()
248 {
249     InputElement::aboutToUnload(this, this);
250 }
251 
shouldUseInputMethod() const252 bool HTMLInputElement::shouldUseInputMethod() const
253 {
254     return m_type == TEXT || m_type == SEARCH || m_type == ISINDEX;
255 }
256 
dispatchFocusEvent()257 void HTMLInputElement::dispatchFocusEvent()
258 {
259     InputElement::dispatchFocusEvent(m_data, this, this);
260 
261     if (isTextField())
262         m_autofilled = false;
263 
264     HTMLFormControlElementWithState::dispatchFocusEvent();
265 }
266 
dispatchBlurEvent()267 void HTMLInputElement::dispatchBlurEvent()
268 {
269     InputElement::dispatchBlurEvent(m_data, this, this);
270     HTMLFormControlElementWithState::dispatchBlurEvent();
271 }
272 
setType(const String & t)273 void HTMLInputElement::setType(const String& t)
274 {
275     if (t.isEmpty()) {
276         int exccode;
277         removeAttribute(typeAttr, exccode);
278     } else
279         setAttribute(typeAttr, t);
280 }
281 
setInputType(const String & t)282 void HTMLInputElement::setInputType(const String& t)
283 {
284     InputType newType;
285 
286     if (equalIgnoringCase(t, "password"))
287 #ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS
288     {
289         if (document()->focusedNode() == this)
290         {
291             android::WebViewCore::getWebViewCore(document()->view())->updateTextfield(this, true, String());
292         }
293 #endif
294         newType = PASSWORD;
295 #ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS
296     }
297 #endif
298     else if (equalIgnoringCase(t, "checkbox"))
299         newType = CHECKBOX;
300     else if (equalIgnoringCase(t, "radio"))
301         newType = RADIO;
302     else if (equalIgnoringCase(t, "submit"))
303         newType = SUBMIT;
304     else if (equalIgnoringCase(t, "reset"))
305         newType = RESET;
306     else if (equalIgnoringCase(t, "file"))
307         newType = FILE;
308     else if (equalIgnoringCase(t, "hidden"))
309         newType = HIDDEN;
310     else if (equalIgnoringCase(t, "image"))
311         newType = IMAGE;
312     else if (equalIgnoringCase(t, "button"))
313         newType = BUTTON;
314     else if (equalIgnoringCase(t, "khtml_isindex"))
315         newType = ISINDEX;
316     else if (equalIgnoringCase(t, "search"))
317         newType = SEARCH;
318     else if (equalIgnoringCase(t, "range"))
319         newType = RANGE;
320     else if (equalIgnoringCase(t, "email"))
321         newType = EMAIL;
322     else if (equalIgnoringCase(t, "number"))
323         newType = NUMBER;
324     else if (equalIgnoringCase(t, "tel"))
325         newType = TELEPHONE;
326     else if (equalIgnoringCase(t, "url"))
327         newType = URL;
328     else
329         newType = TEXT;
330 
331     // IMPORTANT: Don't allow the type to be changed to FILE after the first
332     // type change, otherwise a JavaScript programmer would be able to set a text
333     // field's value to something like /etc/passwd and then change it to a file field.
334     if (inputType() != newType) {
335         if (newType == FILE && m_haveType)
336             // Set the attribute back to the old value.
337             // Useful in case we were called from inside parseMappedAttribute.
338             setAttribute(typeAttr, type());
339         else {
340             checkedRadioButtons(this).removeButton(this);
341 
342             if (newType == FILE && !m_fileList)
343                 m_fileList = FileList::create();
344 
345             bool wasAttached = attached();
346             if (wasAttached)
347                 detach();
348 
349             bool didStoreValue = storesValueSeparateFromAttribute();
350             bool wasPasswordField = inputType() == PASSWORD;
351             bool didRespectHeightAndWidth = respectHeightAndWidthAttrs();
352             m_type = newType;
353             bool willStoreValue = storesValueSeparateFromAttribute();
354             bool isPasswordField = inputType() == PASSWORD;
355             bool willRespectHeightAndWidth = respectHeightAndWidthAttrs();
356 
357             if (didStoreValue && !willStoreValue && !m_data.value().isNull()) {
358                 setAttribute(valueAttr, m_data.value());
359                 m_data.setValue(String());
360             }
361             if (!didStoreValue && willStoreValue)
362                 m_data.setValue(constrainValue(getAttribute(valueAttr)));
363             else
364                 InputElement::updateValueIfNeeded(m_data, this);
365 
366             if (wasPasswordField && !isPasswordField)
367                 unregisterForActivationCallbackIfNeeded();
368             else if (!wasPasswordField && isPasswordField)
369                 registerForActivationCallbackIfNeeded();
370 
371             if (didRespectHeightAndWidth != willRespectHeightAndWidth) {
372                 NamedMappedAttrMap* map = mappedAttributes();
373                 ASSERT(map);
374                 if (Attribute* height = map->getAttributeItem(heightAttr))
375                     attributeChanged(height, false);
376                 if (Attribute* width = map->getAttributeItem(widthAttr))
377                     attributeChanged(width, false);
378                 if (Attribute* align = map->getAttributeItem(alignAttr))
379                     attributeChanged(align, false);
380             }
381 
382             if (wasAttached) {
383                 attach();
384                 if (document()->focusedNode() == this)
385                     updateFocusAppearance(true);
386             }
387 
388             checkedRadioButtons(this).addButton(this);
389         }
390 
391         InputElement::notifyFormStateChanged(this);
392     }
393     m_haveType = true;
394 
395     if (inputType() != IMAGE && m_imageLoader)
396         m_imageLoader.clear();
397 }
398 
formControlType() const399 const AtomicString& HTMLInputElement::formControlType() const
400 {
401     // needs to be lowercase according to DOM spec
402     switch (inputType()) {
403         case BUTTON: {
404             DEFINE_STATIC_LOCAL(const AtomicString, button, ("button"));
405             return button;
406         }
407         case CHECKBOX: {
408             DEFINE_STATIC_LOCAL(const AtomicString, checkbox, ("checkbox"));
409             return checkbox;
410         }
411         case EMAIL: {
412             DEFINE_STATIC_LOCAL(const AtomicString, email, ("email"));
413             return email;
414         }
415         case FILE: {
416             DEFINE_STATIC_LOCAL(const AtomicString, file, ("file"));
417             return file;
418         }
419         case HIDDEN: {
420             DEFINE_STATIC_LOCAL(const AtomicString, hidden, ("hidden"));
421             return hidden;
422         }
423         case IMAGE: {
424             DEFINE_STATIC_LOCAL(const AtomicString, image, ("image"));
425             return image;
426         }
427         case ISINDEX:
428             return emptyAtom;
429         case NUMBER: {
430             DEFINE_STATIC_LOCAL(const AtomicString, number, ("number"));
431             return number;
432         }
433         case PASSWORD: {
434             DEFINE_STATIC_LOCAL(const AtomicString, password, ("password"));
435             return password;
436         }
437         case RADIO: {
438             DEFINE_STATIC_LOCAL(const AtomicString, radio, ("radio"));
439             return radio;
440         }
441         case RANGE: {
442             DEFINE_STATIC_LOCAL(const AtomicString, range, ("range"));
443             return range;
444         }
445         case RESET: {
446             DEFINE_STATIC_LOCAL(const AtomicString, reset, ("reset"));
447             return reset;
448         }
449         case SEARCH: {
450             DEFINE_STATIC_LOCAL(const AtomicString, search, ("search"));
451             return search;
452         }
453         case SUBMIT: {
454             DEFINE_STATIC_LOCAL(const AtomicString, submit, ("submit"));
455             return submit;
456         }
457         case TELEPHONE: {
458             DEFINE_STATIC_LOCAL(const AtomicString, telephone, ("tel"));
459             return telephone;
460         }
461         case TEXT: {
462             DEFINE_STATIC_LOCAL(const AtomicString, text, ("text"));
463             return text;
464         }
465         case URL: {
466             DEFINE_STATIC_LOCAL(const AtomicString, url, ("url"));
467             return url;
468         }
469     }
470     return emptyAtom;
471 }
472 
saveFormControlState(String & result) const473 bool HTMLInputElement::saveFormControlState(String& result) const
474 {
475     if (!autoComplete())
476         return false;
477 
478     switch (inputType()) {
479         case BUTTON:
480         case EMAIL:
481         case FILE:
482         case HIDDEN:
483         case IMAGE:
484         case ISINDEX:
485         case NUMBER:
486         case RANGE:
487         case RESET:
488         case SEARCH:
489         case SUBMIT:
490         case TELEPHONE:
491         case TEXT:
492         case URL:
493             result = value();
494             return true;
495         case CHECKBOX:
496         case RADIO:
497             result = checked() ? "on" : "off";
498             return true;
499         case PASSWORD:
500             return false;
501     }
502     ASSERT_NOT_REACHED();
503     return false;
504 }
505 
restoreFormControlState(const String & state)506 void HTMLInputElement::restoreFormControlState(const String& state)
507 {
508     ASSERT(inputType() != PASSWORD); // should never save/restore password fields
509     switch (inputType()) {
510         case BUTTON:
511         case EMAIL:
512         case FILE:
513         case HIDDEN:
514         case IMAGE:
515         case ISINDEX:
516         case NUMBER:
517         case RANGE:
518         case RESET:
519         case SEARCH:
520         case SUBMIT:
521         case TELEPHONE:
522         case TEXT:
523         case URL:
524             setValue(state);
525             break;
526         case CHECKBOX:
527         case RADIO:
528             setChecked(state == "on");
529             break;
530         case PASSWORD:
531             break;
532     }
533 }
534 
canStartSelection() const535 bool HTMLInputElement::canStartSelection() const
536 {
537     if (!isTextField())
538         return false;
539     return HTMLFormControlElementWithState::canStartSelection();
540 }
541 
canHaveSelection() const542 bool HTMLInputElement::canHaveSelection() const
543 {
544     return isTextField();
545 }
546 
selectionStart() const547 int HTMLInputElement::selectionStart() const
548 {
549     if (!isTextField())
550         return 0;
551     if (document()->focusedNode() != this && m_data.cachedSelectionStart() != -1)
552         return m_data.cachedSelectionStart();
553     if (!renderer())
554         return 0;
555     return toRenderTextControl(renderer())->selectionStart();
556 }
557 
selectionEnd() const558 int HTMLInputElement::selectionEnd() const
559 {
560     if (!isTextField())
561         return 0;
562     if (document()->focusedNode() != this && m_data.cachedSelectionEnd() != -1)
563         return m_data.cachedSelectionEnd();
564     if (!renderer())
565         return 0;
566     return toRenderTextControl(renderer())->selectionEnd();
567 }
568 
isTextFieldWithRenderer(HTMLInputElement * element)569 static bool isTextFieldWithRenderer(HTMLInputElement* element)
570 {
571     if (!element->isTextField())
572         return false;
573 
574     element->document()->updateLayoutIgnorePendingStylesheets();
575     if (!element->renderer())
576         return false;
577 
578     return true;
579 }
580 
setSelectionStart(int start)581 void HTMLInputElement::setSelectionStart(int start)
582 {
583     if (isTextFieldWithRenderer(this))
584         toRenderTextControl(renderer())->setSelectionStart(start);
585 }
586 
setSelectionEnd(int end)587 void HTMLInputElement::setSelectionEnd(int end)
588 {
589     if (isTextFieldWithRenderer(this))
590         toRenderTextControl(renderer())->setSelectionEnd(end);
591 }
592 
select()593 void HTMLInputElement::select()
594 {
595     if (isTextFieldWithRenderer(this))
596         toRenderTextControl(renderer())->select();
597 }
598 
setSelectionRange(int start,int end)599 void HTMLInputElement::setSelectionRange(int start, int end)
600 {
601     InputElement::updateSelectionRange(this, this, start, end);
602 }
603 
accessKeyAction(bool sendToAnyElement)604 void HTMLInputElement::accessKeyAction(bool sendToAnyElement)
605 {
606     switch (inputType()) {
607         case BUTTON:
608         case CHECKBOX:
609         case FILE:
610         case IMAGE:
611         case RADIO:
612         case RANGE:
613         case RESET:
614         case SUBMIT:
615             focus(false);
616             // send the mouse button events iff the caller specified sendToAnyElement
617             dispatchSimulatedClick(0, sendToAnyElement);
618             break;
619         case HIDDEN:
620             // a no-op for this type
621             break;
622         case EMAIL:
623         case ISINDEX:
624         case NUMBER:
625         case PASSWORD:
626         case SEARCH:
627         case TELEPHONE:
628         case TEXT:
629         case URL:
630             // should never restore previous selection here
631             focus(false);
632             break;
633     }
634 }
635 
mapToEntry(const QualifiedName & attrName,MappedAttributeEntry & result) const636 bool HTMLInputElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
637 {
638     if (((attrName == heightAttr || attrName == widthAttr) && respectHeightAndWidthAttrs()) ||
639         attrName == vspaceAttr ||
640         attrName == hspaceAttr) {
641         result = eUniversal;
642         return false;
643     }
644 
645     if (attrName == alignAttr) {
646         if (inputType() == IMAGE) {
647             // Share with <img> since the alignment behavior is the same.
648             result = eReplaced;
649             return false;
650         }
651     }
652 
653     return HTMLElement::mapToEntry(attrName, result);
654 }
655 
parseMappedAttribute(MappedAttribute * attr)656 void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr)
657 {
658     if (attr->name() == nameAttr) {
659         checkedRadioButtons(this).removeButton(this);
660         m_data.setName(attr->value());
661         checkedRadioButtons(this).addButton(this);
662     } else if (attr->name() == autocompleteAttr) {
663         if (equalIgnoringCase(attr->value(), "off")) {
664             m_autocomplete = Off;
665             registerForActivationCallbackIfNeeded();
666         } else {
667             if (m_autocomplete == Off)
668                 unregisterForActivationCallbackIfNeeded();
669             if (attr->isEmpty())
670                 m_autocomplete = Uninitialized;
671             else
672                 m_autocomplete = On;
673         }
674     } else if (attr->name() == typeAttr) {
675         setInputType(attr->value());
676     } else if (attr->name() == valueAttr) {
677         // We only need to setChanged if the form is looking at the default value right now.
678         if (m_data.value().isNull())
679             setNeedsStyleRecalc();
680         setFormControlValueMatchesRenderer(false);
681     } else if (attr->name() == checkedAttr) {
682         m_defaultChecked = !attr->isNull();
683         if (m_useDefaultChecked) {
684             setChecked(m_defaultChecked);
685             m_useDefaultChecked = true;
686         }
687     } else if (attr->name() == maxlengthAttr)
688         InputElement::parseMaxLengthAttribute(m_data, this, this, attr);
689     else if (attr->name() == sizeAttr)
690         InputElement::parseSizeAttribute(m_data, this, attr);
691     else if (attr->name() == altAttr) {
692         if (renderer() && inputType() == IMAGE)
693             toRenderImage(renderer())->updateAltText();
694     } else if (attr->name() == srcAttr) {
695         if (renderer() && inputType() == IMAGE) {
696             if (!m_imageLoader)
697                 m_imageLoader.set(new HTMLImageLoader(this));
698             m_imageLoader->updateFromElementIgnoringPreviousError();
699         }
700     } else if (attr->name() == usemapAttr ||
701                attr->name() == accesskeyAttr) {
702         // FIXME: ignore for the moment
703     } else if (attr->name() == vspaceAttr) {
704         addCSSLength(attr, CSSPropertyMarginTop, attr->value());
705         addCSSLength(attr, CSSPropertyMarginBottom, attr->value());
706     } else if (attr->name() == hspaceAttr) {
707         addCSSLength(attr, CSSPropertyMarginLeft, attr->value());
708         addCSSLength(attr, CSSPropertyMarginRight, attr->value());
709     } else if (attr->name() == alignAttr) {
710         if (inputType() == IMAGE)
711             addHTMLAlignment(attr);
712     } else if (attr->name() == widthAttr) {
713         if (respectHeightAndWidthAttrs())
714             addCSSLength(attr, CSSPropertyWidth, attr->value());
715     } else if (attr->name() == heightAttr) {
716         if (respectHeightAndWidthAttrs())
717             addCSSLength(attr, CSSPropertyHeight, attr->value());
718     } else if (attr->name() == onfocusAttr) {
719         setAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(this, attr));
720     } else if (attr->name() == onblurAttr) {
721         setAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(this, attr));
722     } else if (attr->name() == onselectAttr) {
723         setAttributeEventListener(eventNames().selectEvent, createAttributeEventListener(this, attr));
724     } else if (attr->name() == onchangeAttr) {
725         setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr));
726     }
727     // Search field and slider attributes all just cause updateFromElement to be called through style
728     // recalcing.
729     else if (attr->name() == onsearchAttr) {
730         setAttributeEventListener(eventNames().searchEvent, createAttributeEventListener(this, attr));
731     } else if (attr->name() == resultsAttr) {
732         int oldResults = m_maxResults;
733         m_maxResults = !attr->isNull() ? min(attr->value().toInt(), maxSavedResults) : -1;
734         // FIXME: Detaching just for maxResults change is not ideal.  We should figure out the right
735         // time to relayout for this change.
736         if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0) && attached()) {
737             detach();
738             attach();
739         }
740         setNeedsStyleRecalc();
741     } else if (attr->name() == placeholderAttr) {
742         if (isTextField())
743             updatePlaceholderVisibility();
744     } else if (attr->name() == autosaveAttr ||
745              attr->name() == incrementalAttr ||
746              attr->name() == minAttr ||
747              attr->name() == maxAttr ||
748              attr->name() == multipleAttr ||
749              attr->name() == precisionAttr)
750         setNeedsStyleRecalc();
751     else
752         HTMLFormControlElementWithState::parseMappedAttribute(attr);
753 }
754 
rendererIsNeeded(RenderStyle * style)755 bool HTMLInputElement::rendererIsNeeded(RenderStyle *style)
756 {
757     switch (inputType()) {
758         case BUTTON:
759         case CHECKBOX:
760         case EMAIL:
761         case FILE:
762         case IMAGE:
763         case ISINDEX:
764         case NUMBER:
765         case PASSWORD:
766         case RADIO:
767         case RANGE:
768         case RESET:
769         case SEARCH:
770         case SUBMIT:
771         case TELEPHONE:
772         case TEXT:
773         case URL:
774             return HTMLFormControlElementWithState::rendererIsNeeded(style);
775         case HIDDEN:
776             return false;
777     }
778     ASSERT(false);
779     return false;
780 }
781 
createRenderer(RenderArena * arena,RenderStyle * style)782 RenderObject *HTMLInputElement::createRenderer(RenderArena *arena, RenderStyle *style)
783 {
784     switch (inputType()) {
785         case BUTTON:
786         case RESET:
787         case SUBMIT:
788             return new (arena) RenderButton(this);
789         case CHECKBOX:
790         case RADIO:
791             return RenderObject::createObject(this, style);
792         case FILE:
793             return new (arena) RenderFileUploadControl(this);
794         case HIDDEN:
795             break;
796         case IMAGE:
797             return new (arena) RenderImage(this);
798         case RANGE:
799             return new (arena) RenderSlider(this);
800         case EMAIL:
801         case ISINDEX:
802         case NUMBER:
803         case PASSWORD:
804         case SEARCH:
805         case TELEPHONE:
806         case TEXT:
807         case URL:
808             return new (arena) RenderTextControlSingleLine(this);
809     }
810     ASSERT(false);
811     return 0;
812 }
813 
attach()814 void HTMLInputElement::attach()
815 {
816     if (!m_inited) {
817         if (!m_haveType)
818             setInputType(getAttribute(typeAttr));
819         m_inited = true;
820     }
821 
822     HTMLFormControlElementWithState::attach();
823 
824     if (inputType() == IMAGE) {
825         if (!m_imageLoader)
826             m_imageLoader.set(new HTMLImageLoader(this));
827         m_imageLoader->updateFromElement();
828         if (renderer()) {
829             RenderImage* imageObj = toRenderImage(renderer());
830             imageObj->setCachedImage(m_imageLoader->image());
831 
832             // If we have no image at all because we have no src attribute, set
833             // image height and width for the alt text instead.
834             if (!m_imageLoader->image() && !imageObj->cachedImage())
835                 imageObj->setImageSizeForAltText();
836         }
837     }
838 }
839 
detach()840 void HTMLInputElement::detach()
841 {
842     HTMLFormControlElementWithState::detach();
843     setFormControlValueMatchesRenderer(false);
844 }
845 
altText() const846 String HTMLInputElement::altText() const
847 {
848     // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
849     // also heavily discussed by Hixie on bugzilla
850     // note this is intentionally different to HTMLImageElement::altText()
851     String alt = getAttribute(altAttr);
852     // fall back to title attribute
853     if (alt.isNull())
854         alt = getAttribute(titleAttr);
855     if (alt.isNull())
856         alt = getAttribute(valueAttr);
857     if (alt.isEmpty())
858         alt = inputElementAltText();
859     return alt;
860 }
861 
isSuccessfulSubmitButton() const862 bool HTMLInputElement::isSuccessfulSubmitButton() const
863 {
864     // HTML spec says that buttons must have names to be considered successful.
865     // However, other browsers do not impose this constraint. So we do likewise.
866     return !disabled() && (inputType() == IMAGE || inputType() == SUBMIT);
867 }
868 
isActivatedSubmit() const869 bool HTMLInputElement::isActivatedSubmit() const
870 {
871     return m_activeSubmit;
872 }
873 
setActivatedSubmit(bool flag)874 void HTMLInputElement::setActivatedSubmit(bool flag)
875 {
876     m_activeSubmit = flag;
877 }
878 
appendFormData(FormDataList & encoding,bool multipart)879 bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart)
880 {
881     // image generates its own names, but for other types there is no form data unless there's a name
882     if (name().isEmpty() && inputType() != IMAGE)
883         return false;
884 
885     switch (inputType()) {
886         case EMAIL:
887         case HIDDEN:
888         case ISINDEX:
889         case NUMBER:
890         case PASSWORD:
891         case RANGE:
892         case SEARCH:
893         case TELEPHONE:
894         case TEXT:
895         case URL:
896             // always successful
897             encoding.appendData(name(), value());
898             return true;
899 
900         case CHECKBOX:
901         case RADIO:
902             if (checked()) {
903                 encoding.appendData(name(), value());
904                 return true;
905             }
906             break;
907 
908         case BUTTON:
909         case RESET:
910             // these types of buttons are never successful
911             return false;
912 
913         case IMAGE:
914             if (m_activeSubmit) {
915                 encoding.appendData(name().isEmpty() ? "x" : (name() + ".x"), m_xPos);
916                 encoding.appendData(name().isEmpty() ? "y" : (name() + ".y"), m_yPos);
917                 if (!name().isEmpty() && !value().isEmpty())
918                     encoding.appendData(name(), value());
919                 return true;
920             }
921             break;
922 
923         case SUBMIT:
924             if (m_activeSubmit) {
925                 String enc_str = valueWithDefault();
926                 encoding.appendData(name(), enc_str);
927                 return true;
928             }
929             break;
930 
931         case FILE: {
932             unsigned numFiles = m_fileList->length();
933             if (!multipart) {
934                 // Send only the basenames.
935                 // 4.10.16.4 and 4.10.16.6 sections in HTML5.
936 
937                 // Unlike the multipart case, we have no special
938                 // handling for the empty fileList because Netscape
939                 // doesn't support for non-multipart submission of
940                 // file inputs, and Firefox doesn't add "name=" query
941                 // parameter.
942 
943                 for (unsigned i = 0; i < numFiles; ++i) {
944                     encoding.appendData(name(), m_fileList->item(i)->fileName());
945                 }
946                 return true;
947             }
948 
949             // If no filename at all is entered, return successful but empty.
950             // Null would be more logical, but Netscape posts an empty file. Argh.
951             if (!numFiles) {
952                 encoding.appendFile(name(), File::create(""));
953                 return true;
954             }
955 
956             for (unsigned i = 0; i < numFiles; ++i)
957                 encoding.appendFile(name(), m_fileList->item(i));
958             return true;
959         }
960     }
961     return false;
962 }
963 
reset()964 void HTMLInputElement::reset()
965 {
966     if (storesValueSeparateFromAttribute())
967         setValue(String());
968 
969     setChecked(m_defaultChecked);
970     m_useDefaultChecked = true;
971 }
972 
setChecked(bool nowChecked,bool sendChangeEvent)973 void HTMLInputElement::setChecked(bool nowChecked, bool sendChangeEvent)
974 {
975     if (checked() == nowChecked)
976         return;
977 
978     checkedRadioButtons(this).removeButton(this);
979 
980     m_useDefaultChecked = false;
981     m_checked = nowChecked;
982     setNeedsStyleRecalc();
983 
984     checkedRadioButtons(this).addButton(this);
985 
986     if (renderer() && renderer()->style()->hasAppearance())
987         renderer()->theme()->stateChanged(renderer(), CheckedState);
988 
989     // Ideally we'd do this from the render tree (matching
990     // RenderTextView), but it's not possible to do it at the moment
991     // because of the way the code is structured.
992     if (renderer() && AXObjectCache::accessibilityEnabled())
993         renderer()->document()->axObjectCache()->postNotification(renderer(), "AXCheckedStateChanged", true);
994 
995     // Only send a change event for items in the document (avoid firing during
996     // parsing) and don't send a change event for a radio button that's getting
997     // unchecked to match other browsers. DOM is not a useful standard for this
998     // because it says only to fire change events at "lose focus" time, which is
999     // definitely wrong in practice for these types of elements.
1000     if (sendChangeEvent && inDocument() && (inputType() != RADIO || nowChecked))
1001         dispatchFormControlChangeEvent();
1002 }
1003 
setIndeterminate(bool _indeterminate)1004 void HTMLInputElement::setIndeterminate(bool _indeterminate)
1005 {
1006     // Only checkboxes honor indeterminate.
1007     if (inputType() != CHECKBOX || indeterminate() == _indeterminate)
1008         return;
1009 
1010     m_indeterminate = _indeterminate;
1011 
1012     setNeedsStyleRecalc();
1013 
1014     if (renderer() && renderer()->style()->hasAppearance())
1015         renderer()->theme()->stateChanged(renderer(), CheckedState);
1016 }
1017 
size() const1018 int HTMLInputElement::size() const
1019 {
1020     return m_data.size();
1021 }
1022 
copyNonAttributeProperties(const Element * source)1023 void HTMLInputElement::copyNonAttributeProperties(const Element* source)
1024 {
1025     const HTMLInputElement* sourceElement = static_cast<const HTMLInputElement*>(source);
1026 
1027     m_data.setValue(sourceElement->m_data.value());
1028     m_checked = sourceElement->m_checked;
1029     m_indeterminate = sourceElement->m_indeterminate;
1030 
1031     HTMLFormControlElementWithState::copyNonAttributeProperties(source);
1032 }
1033 
value() const1034 String HTMLInputElement::value() const
1035 {
1036     // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't applicable to the file upload control
1037     // but we don't want to break existing websites, who may be relying on being able to get the file name as a value.
1038     if (inputType() == FILE) {
1039         if (!m_fileList->isEmpty())
1040             return m_fileList->item(0)->fileName();
1041         return String();
1042     }
1043 
1044     String value = m_data.value();
1045     if (value.isNull()) {
1046         value = constrainValue(getAttribute(valueAttr));
1047 
1048         // If no attribute exists, then just use "on" or "" based off the checked() state of the control.
1049         if (value.isNull() && (inputType() == CHECKBOX || inputType() == RADIO))
1050             return checked() ? "on" : "";
1051     }
1052 
1053     return value;
1054 }
1055 
valueWithDefault() const1056 String HTMLInputElement::valueWithDefault() const
1057 {
1058     String v = value();
1059     if (v.isNull()) {
1060         switch (inputType()) {
1061             case BUTTON:
1062             case CHECKBOX:
1063             case EMAIL:
1064             case FILE:
1065             case HIDDEN:
1066             case IMAGE:
1067             case ISINDEX:
1068             case NUMBER:
1069             case PASSWORD:
1070             case RADIO:
1071             case RANGE:
1072             case SEARCH:
1073             case TELEPHONE:
1074             case TEXT:
1075             case URL:
1076                 break;
1077             case RESET:
1078                 v = resetButtonDefaultLabel();
1079                 break;
1080             case SUBMIT:
1081                 v = submitButtonDefaultLabel();
1082                 break;
1083         }
1084     }
1085     return v;
1086 }
1087 
setValue(const String & value)1088 void HTMLInputElement::setValue(const String& value)
1089 {
1090     // For security reasons, we don't allow setting the filename, but we do allow clearing it.
1091     // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't applicable to the file upload control
1092     // but we don't want to break existing websites, who may be relying on this method to clear things.
1093     if (inputType() == FILE && !value.isEmpty())
1094         return;
1095 
1096     setFormControlValueMatchesRenderer(false);
1097     if (storesValueSeparateFromAttribute()) {
1098         if (inputType() == FILE)
1099             m_fileList->clear();
1100         else {
1101             m_data.setValue(constrainValue(value));
1102             if (isTextField()) {
1103                 InputElement::updatePlaceholderVisibility(m_data, this, this);
1104                 if (inDocument())
1105                     document()->updateStyleIfNeeded();
1106             }
1107         }
1108         if (renderer())
1109             renderer()->updateFromElement();
1110         setNeedsStyleRecalc();
1111     } else
1112         setAttribute(valueAttr, constrainValue(value));
1113 
1114     if (isTextField()) {
1115         unsigned max = m_data.value().length();
1116 #ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS
1117         // Make sure our UI side textfield changes to match the RenderTextControl
1118         android::WebViewCore::getWebViewCore(document()->view())->updateTextfield(this, false, value);
1119 #endif
1120         if (document()->focusedNode() == this)
1121             InputElement::updateSelectionRange(this, this, max, max);
1122         else
1123             cacheSelection(max, max);
1124     }
1125     InputElement::notifyFormStateChanged(this);
1126 }
1127 
placeholder() const1128 String HTMLInputElement::placeholder() const
1129 {
1130     return getAttribute(placeholderAttr).string();
1131 }
1132 
setPlaceholder(const String & value)1133 void HTMLInputElement::setPlaceholder(const String& value)
1134 {
1135     setAttribute(placeholderAttr, value);
1136 }
1137 
searchEventsShouldBeDispatched() const1138 bool HTMLInputElement::searchEventsShouldBeDispatched() const
1139 {
1140     return hasAttribute(incrementalAttr);
1141 }
1142 
setValueFromRenderer(const String & value)1143 void HTMLInputElement::setValueFromRenderer(const String& value)
1144 {
1145     // File upload controls will always use setFileListFromRenderer.
1146     ASSERT(inputType() != FILE);
1147     InputElement::setValueFromRenderer(m_data, this, this, value);
1148 }
1149 
setFileListFromRenderer(const Vector<String> & paths)1150 void HTMLInputElement::setFileListFromRenderer(const Vector<String>& paths)
1151 {
1152     m_fileList->clear();
1153     int size = paths.size();
1154     for (int i = 0; i < size; i++)
1155         m_fileList->append(File::create(paths[i]));
1156 
1157     setFormControlValueMatchesRenderer(true);
1158     InputElement::notifyFormStateChanged(this);
1159 }
1160 
storesValueSeparateFromAttribute() const1161 bool HTMLInputElement::storesValueSeparateFromAttribute() const
1162 {
1163     switch (inputType()) {
1164         case BUTTON:
1165         case CHECKBOX:
1166         case HIDDEN:
1167         case IMAGE:
1168         case RADIO:
1169         case RESET:
1170         case SUBMIT:
1171             return false;
1172         case EMAIL:
1173         case FILE:
1174         case ISINDEX:
1175         case NUMBER:
1176         case PASSWORD:
1177         case RANGE:
1178         case SEARCH:
1179         case TELEPHONE:
1180         case TEXT:
1181         case URL:
1182             return true;
1183     }
1184     return false;
1185 }
1186 
preDispatchEventHandler(Event * evt)1187 void* HTMLInputElement::preDispatchEventHandler(Event *evt)
1188 {
1189     // preventDefault or "return false" are used to reverse the automatic checking/selection we do here.
1190     // This result gives us enough info to perform the "undo" in postDispatch of the action we take here.
1191     void* result = 0;
1192     if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
1193             && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1194         if (inputType() == CHECKBOX) {
1195             // As a way to store the state, we return 0 if we were unchecked, 1 if we were checked, and 2 for
1196             // indeterminate.
1197             if (indeterminate()) {
1198                 result = (void*)0x2;
1199                 setIndeterminate(false);
1200             } else {
1201                 if (checked())
1202                     result = (void*)0x1;
1203                 setChecked(!checked(), true);
1204             }
1205         } else {
1206             // For radio buttons, store the current selected radio object.
1207             // We really want radio groups to end up in sane states, i.e., to have something checked.
1208             // Therefore if nothing is currently selected, we won't allow this action to be "undone", since
1209             // we want some object in the radio group to actually get selected.
1210             HTMLInputElement* currRadio = checkedRadioButtons(this).checkedButtonForGroup(name());
1211             if (currRadio) {
1212                 // We have a radio button selected that is not us.  Cache it in our result field and ref it so
1213                 // that it can't be destroyed.
1214                 currRadio->ref();
1215                 result = currRadio;
1216             }
1217             setChecked(true, true);
1218         }
1219     }
1220     return result;
1221 }
1222 
postDispatchEventHandler(Event * evt,void * data)1223 void HTMLInputElement::postDispatchEventHandler(Event *evt, void* data)
1224 {
1225     if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
1226             && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1227         if (inputType() == CHECKBOX) {
1228             // Reverse the checking we did in preDispatch.
1229             if (evt->defaultPrevented() || evt->defaultHandled()) {
1230                 if (data == (void*)0x2)
1231                     setIndeterminate(true);
1232                 else
1233                     setChecked(data);
1234             }
1235         } else if (data) {
1236             HTMLInputElement* input = static_cast<HTMLInputElement*>(data);
1237             if (evt->defaultPrevented() || evt->defaultHandled()) {
1238                 // Restore the original selected radio button if possible.
1239                 // Make sure it is still a radio button and only do the restoration if it still
1240                 // belongs to our group.
1241 
1242                 if (input->form() == form() && input->inputType() == RADIO && input->name() == name()) {
1243                     // Ok, the old radio button is still in our form and in our group and is still a
1244                     // radio button, so it's safe to restore selection to it.
1245                     input->setChecked(true);
1246                 }
1247             }
1248             input->deref();
1249         }
1250 
1251         // Left clicks on radio buttons and check boxes already performed default actions in preDispatchEventHandler().
1252         evt->setDefaultHandled();
1253     }
1254 }
1255 
defaultEventHandler(Event * evt)1256 void HTMLInputElement::defaultEventHandler(Event* evt)
1257 {
1258     // FIXME: It would be better to refactor this for the different types of input element.
1259     // Having them all in one giant function makes this hard to read, and almost all the handling is type-specific.
1260 
1261     bool clickDefaultFormButton = false;
1262 
1263     if (isTextField() && evt->type() == eventNames().textInputEvent && evt->isTextEvent() && static_cast<TextEvent*>(evt)->data() == "\n")
1264         clickDefaultFormButton = true;
1265 
1266     if (inputType() == IMAGE && evt->isMouseEvent() && evt->type() == eventNames().clickEvent) {
1267         // record the mouse position for when we get the DOMActivate event
1268         MouseEvent* me = static_cast<MouseEvent*>(evt);
1269         // FIXME: We could just call offsetX() and offsetY() on the event,
1270         // but that's currently broken, so for now do the computation here.
1271         if (me->isSimulated() || !renderer()) {
1272             m_xPos = 0;
1273             m_yPos = 0;
1274         } else {
1275             // FIXME: This doesn't work correctly with transforms.
1276             // FIXME: pageX/pageY need adjusting for pageZoomFactor(). Use actualPageLocation()?
1277             IntPoint absOffset = roundedIntPoint(renderer()->localToAbsolute());
1278             m_xPos = me->pageX() - absOffset.x();
1279             m_yPos = me->pageY() - absOffset.y();
1280         }
1281     }
1282 
1283     if (isTextField()
1284             && evt->type() == eventNames().keydownEvent
1285             && evt->isKeyboardEvent()
1286             && focused()
1287             && document()->frame()
1288             && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) {
1289         evt->setDefaultHandled();
1290         return;
1291     }
1292 
1293     if (inputType() == RADIO
1294             && evt->isMouseEvent()
1295             && evt->type() == eventNames().clickEvent
1296             && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
1297         evt->setDefaultHandled();
1298         return;
1299     }
1300 
1301     // Call the base event handler before any of our own event handling for almost all events in text fields.
1302     // Makes editing keyboard handling take precedence over the keydown and keypress handling in this function.
1303     bool callBaseClassEarly = isTextField() && !clickDefaultFormButton
1304         && (evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent);
1305     if (callBaseClassEarly) {
1306         HTMLFormControlElementWithState::defaultEventHandler(evt);
1307         if (evt->defaultHandled())
1308             return;
1309     }
1310 
1311     // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means
1312     // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks
1313     // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element
1314     // must dispatch a DOMActivate event - a click event will not do the job.
1315     if (evt->type() == eventNames().DOMActivateEvent && !disabled()) {
1316         if (inputType() == IMAGE || inputType() == SUBMIT || inputType() == RESET) {
1317             if (!form())
1318                 return;
1319             if (inputType() == RESET)
1320                 form()->reset();
1321             else {
1322                 m_activeSubmit = true;
1323                 // FIXME: Would be cleaner to get m_xPos and m_yPos out of the underlying mouse
1324                 // event (if any) here instead of relying on the variables set above when
1325                 // processing the click event. Even better, appendFormData could pass the
1326                 // event in, and then we could get rid of m_xPos and m_yPos altogether!
1327                 if (!form()->prepareSubmit(evt)) {
1328                     m_xPos = 0;
1329                     m_yPos = 0;
1330                 }
1331                 m_activeSubmit = false;
1332             }
1333         } else if (inputType() == FILE && renderer())
1334             toRenderFileUploadControl(renderer())->click();
1335     }
1336 
1337     // Use key press event here since sending simulated mouse events
1338     // on key down blocks the proper sending of the key press event.
1339     if (evt->type() == eventNames().keypressEvent && evt->isKeyboardEvent()) {
1340         bool clickElement = false;
1341 
1342         int charCode = static_cast<KeyboardEvent*>(evt)->charCode();
1343 
1344         if (charCode == '\r') {
1345             switch (inputType()) {
1346                 case CHECKBOX:
1347                 case EMAIL:
1348                 case HIDDEN:
1349                 case ISINDEX:
1350                 case NUMBER:
1351                 case PASSWORD:
1352                 case RANGE:
1353                 case SEARCH:
1354                 case TELEPHONE:
1355                 case TEXT:
1356                 case URL:
1357                     // Simulate mouse click on the default form button for enter for these types of elements.
1358                     clickDefaultFormButton = true;
1359                     break;
1360                 case BUTTON:
1361                 case FILE:
1362                 case IMAGE:
1363                 case RESET:
1364                 case SUBMIT:
1365                     // Simulate mouse click for enter for these types of elements.
1366                     clickElement = true;
1367                     break;
1368                 case RADIO:
1369                     break; // Don't do anything for enter on a radio button.
1370             }
1371         } else if (charCode == ' ') {
1372             switch (inputType()) {
1373                 case BUTTON:
1374                 case CHECKBOX:
1375                 case FILE:
1376                 case IMAGE:
1377                 case RESET:
1378                 case SUBMIT:
1379                 case RADIO:
1380                     // Prevent scrolling down the page.
1381                     evt->setDefaultHandled();
1382                     return;
1383                 default:
1384                     break;
1385             }
1386         }
1387 
1388         if (clickElement) {
1389             dispatchSimulatedClick(evt);
1390             evt->setDefaultHandled();
1391             return;
1392         }
1393     }
1394 
1395     if (evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent()) {
1396         String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
1397 
1398         if (key == "U+0020") {
1399             switch (inputType()) {
1400                 case BUTTON:
1401                 case CHECKBOX:
1402                 case FILE:
1403                 case IMAGE:
1404                 case RESET:
1405                 case SUBMIT:
1406                 case RADIO:
1407                     setActive(true, true);
1408                     // No setDefaultHandled(), because IE dispatches a keypress in this case
1409                     // and the caller will only dispatch a keypress if we don't call setDefaultHandled.
1410                     return;
1411                 default:
1412                     break;
1413             }
1414         }
1415 
1416 // allow enter to change state of radio
1417         if (inputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) {
1418             // Left and up mean "previous radio button".
1419             // Right and down mean "next radio button".
1420             // Tested in WinIE, and even for RTL, left still means previous radio button (and so moves
1421             // to the right).  Seems strange, but we'll match it.
1422             bool forward = (key == "Down" || key == "Right");
1423 
1424             // We can only stay within the form's children if the form hasn't been demoted to a leaf because
1425             // of malformed HTML.
1426             Node* n = this;
1427             while ((n = (forward ? n->traverseNextNode() : n->traversePreviousNode()))) {
1428                 // Once we encounter a form element, we know we're through.
1429                 if (n->hasTagName(formTag))
1430                     break;
1431 
1432                 // Look for more radio buttons.
1433                 if (n->hasTagName(inputTag)) {
1434                     HTMLInputElement* elt = static_cast<HTMLInputElement*>(n);
1435                     if (elt->form() != form())
1436                         break;
1437                     if (n->hasTagName(inputTag)) {
1438                         HTMLInputElement* inputElt = static_cast<HTMLInputElement*>(n);
1439                         if (inputElt->inputType() == RADIO && inputElt->name() == name() && inputElt->isFocusable()) {
1440                             inputElt->setChecked(true);
1441                             document()->setFocusedNode(inputElt);
1442                             inputElt->dispatchSimulatedClick(evt, false, false);
1443                             evt->setDefaultHandled();
1444                             break;
1445                         }
1446                     }
1447                 }
1448             }
1449         }
1450     }
1451 
1452     if (evt->type() == eventNames().keyupEvent && evt->isKeyboardEvent()) {
1453         bool clickElement = false;
1454 
1455         String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
1456 
1457         if (key == "U+0020") {
1458             switch (inputType()) {
1459                 case BUTTON:
1460                 case CHECKBOX:
1461                 case FILE:
1462                 case IMAGE:
1463                 case RESET:
1464                 case SUBMIT:
1465                     // Simulate mouse click for spacebar for these types of elements.
1466                     // The AppKit already does this for some, but not all, of them.
1467                     clickElement = true;
1468                     break;
1469                 case RADIO:
1470                     // If an unselected radio is tabbed into (because the entire group has nothing
1471                     // checked, or because of some explicit .focus() call), then allow space to check it.
1472                     if (!checked())
1473                         clickElement = true;
1474                     break;
1475                 case EMAIL:
1476                 case HIDDEN:
1477                 case ISINDEX:
1478                 case NUMBER:
1479                 case PASSWORD:
1480                 case RANGE:
1481                 case SEARCH:
1482                 case TELEPHONE:
1483                 case TEXT:
1484                 case URL:
1485                     break;
1486             }
1487         }
1488 
1489         if (clickElement) {
1490             if (active())
1491                 dispatchSimulatedClick(evt);
1492             evt->setDefaultHandled();
1493             return;
1494         }
1495     }
1496 
1497     if (clickDefaultFormButton) {
1498         if (isSearchField()) {
1499             addSearchResult();
1500             onSearch();
1501         }
1502         // Fire onChange for text fields.
1503         RenderObject* r = renderer();
1504         if (r && r->isTextField() && toRenderTextControl(r)->isEdited()) {
1505             dispatchFormControlChangeEvent();
1506             // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it.
1507             r = renderer();
1508             if (r && r->isTextField())
1509                 toRenderTextControl(r)->setEdited(false);
1510         }
1511         // Form may never have been present, or may have been destroyed by the change event.
1512         if (form())
1513             form()->submitClick(evt);
1514         evt->setDefaultHandled();
1515         return;
1516     }
1517 
1518     if (evt->isBeforeTextInsertedEvent())
1519         InputElement::handleBeforeTextInsertedEvent(m_data, this, document(), evt);
1520 
1521     if (isTextField() && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == eventNames().blurEvent || evt->type() == eventNames().focusEvent))
1522         toRenderTextControlSingleLine(renderer())->forwardEvent(evt);
1523 
1524     if (inputType() == RANGE && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent()))
1525         toRenderSlider(renderer())->forwardEvent(evt);
1526 
1527     if (!callBaseClassEarly && !evt->defaultHandled())
1528         HTMLFormControlElementWithState::defaultEventHandler(evt);
1529 }
1530 
isURLAttribute(Attribute * attr) const1531 bool HTMLInputElement::isURLAttribute(Attribute *attr) const
1532 {
1533     return (attr->name() == srcAttr);
1534 }
1535 
defaultValue() const1536 String HTMLInputElement::defaultValue() const
1537 {
1538     return getAttribute(valueAttr);
1539 }
1540 
setDefaultValue(const String & value)1541 void HTMLInputElement::setDefaultValue(const String &value)
1542 {
1543     setAttribute(valueAttr, value);
1544 }
1545 
defaultChecked() const1546 bool HTMLInputElement::defaultChecked() const
1547 {
1548     return !getAttribute(checkedAttr).isNull();
1549 }
1550 
setDefaultChecked(bool defaultChecked)1551 void HTMLInputElement::setDefaultChecked(bool defaultChecked)
1552 {
1553     setAttribute(checkedAttr, defaultChecked ? "" : 0);
1554 }
1555 
setDefaultName(const AtomicString & name)1556 void HTMLInputElement::setDefaultName(const AtomicString& name)
1557 {
1558     m_data.setName(name);
1559 }
1560 
accept() const1561 String HTMLInputElement::accept() const
1562 {
1563     return getAttribute(acceptAttr);
1564 }
1565 
setAccept(const String & value)1566 void HTMLInputElement::setAccept(const String &value)
1567 {
1568     setAttribute(acceptAttr, value);
1569 }
1570 
accessKey() const1571 String HTMLInputElement::accessKey() const
1572 {
1573     return getAttribute(accesskeyAttr);
1574 }
1575 
setAccessKey(const String & value)1576 void HTMLInputElement::setAccessKey(const String &value)
1577 {
1578     setAttribute(accesskeyAttr, value);
1579 }
1580 
align() const1581 String HTMLInputElement::align() const
1582 {
1583     return getAttribute(alignAttr);
1584 }
1585 
setAlign(const String & value)1586 void HTMLInputElement::setAlign(const String &value)
1587 {
1588     setAttribute(alignAttr, value);
1589 }
1590 
alt() const1591 String HTMLInputElement::alt() const
1592 {
1593     return getAttribute(altAttr);
1594 }
1595 
setAlt(const String & value)1596 void HTMLInputElement::setAlt(const String &value)
1597 {
1598     setAttribute(altAttr, value);
1599 }
1600 
maxLength() const1601 int HTMLInputElement::maxLength() const
1602 {
1603     return m_data.maxLength();
1604 }
1605 
setMaxLength(int _maxLength)1606 void HTMLInputElement::setMaxLength(int _maxLength)
1607 {
1608     setAttribute(maxlengthAttr, String::number(_maxLength));
1609 }
1610 
multiple() const1611 bool HTMLInputElement::multiple() const
1612 {
1613     return !getAttribute(multipleAttr).isNull();
1614 }
1615 
setMultiple(bool multiple)1616 void HTMLInputElement::setMultiple(bool multiple)
1617 {
1618     setAttribute(multipleAttr, multiple ? "" : 0);
1619 }
1620 
setSize(unsigned _size)1621 void HTMLInputElement::setSize(unsigned _size)
1622 {
1623     setAttribute(sizeAttr, String::number(_size));
1624 }
1625 
src() const1626 KURL HTMLInputElement::src() const
1627 {
1628     return document()->completeURL(getAttribute(srcAttr));
1629 }
1630 
setSrc(const String & value)1631 void HTMLInputElement::setSrc(const String &value)
1632 {
1633     setAttribute(srcAttr, value);
1634 }
1635 
useMap() const1636 String HTMLInputElement::useMap() const
1637 {
1638     return getAttribute(usemapAttr);
1639 }
1640 
setUseMap(const String & value)1641 void HTMLInputElement::setUseMap(const String &value)
1642 {
1643     setAttribute(usemapAttr, value);
1644 }
1645 
setAutofilled(bool b)1646 void HTMLInputElement::setAutofilled(bool b)
1647 {
1648     if (b == m_autofilled)
1649         return;
1650 
1651     m_autofilled = b;
1652     setNeedsStyleRecalc();
1653 }
1654 
files()1655 FileList* HTMLInputElement::files()
1656 {
1657     if (inputType() != FILE)
1658         return 0;
1659     return m_fileList.get();
1660 }
1661 
constrainValue(const String & proposedValue) const1662 String HTMLInputElement::constrainValue(const String& proposedValue) const
1663 {
1664     return InputElement::constrainValue(this, proposedValue, m_data.maxLength());
1665 }
1666 
needsActivationCallback()1667 bool HTMLInputElement::needsActivationCallback()
1668 {
1669     return inputType() == PASSWORD || m_autocomplete == Off;
1670 }
1671 
registerForActivationCallbackIfNeeded()1672 void HTMLInputElement::registerForActivationCallbackIfNeeded()
1673 {
1674     if (needsActivationCallback())
1675         document()->registerForDocumentActivationCallbacks(this);
1676 }
1677 
unregisterForActivationCallbackIfNeeded()1678 void HTMLInputElement::unregisterForActivationCallbackIfNeeded()
1679 {
1680     if (!needsActivationCallback())
1681         document()->unregisterForDocumentActivationCallbacks(this);
1682 }
1683 
isRequiredFormControl() const1684 bool HTMLInputElement::isRequiredFormControl() const
1685 {
1686     if (!required())
1687         return false;
1688 
1689     switch (inputType()) {
1690         case TEXT:
1691         case SEARCH:
1692         case URL:
1693         case TELEPHONE:
1694         case EMAIL:
1695         case PASSWORD:
1696         case NUMBER:
1697         case CHECKBOX:
1698         case RADIO:
1699         case FILE:
1700             return true;
1701         case HIDDEN:
1702         case RANGE:
1703         case SUBMIT:
1704         case IMAGE:
1705         case RESET:
1706         case BUTTON:
1707         case ISINDEX:
1708             return false;
1709     }
1710 
1711     ASSERT_NOT_REACHED();
1712     return false;
1713 }
1714 
cacheSelection(int start,int end)1715 void HTMLInputElement::cacheSelection(int start, int end)
1716 {
1717     m_data.setCachedSelectionStart(start);
1718     m_data.setCachedSelectionEnd(end);
1719 }
1720 
addSearchResult()1721 void HTMLInputElement::addSearchResult()
1722 {
1723     ASSERT(isSearchField());
1724     if (renderer())
1725         toRenderTextControlSingleLine(renderer())->addSearchResult();
1726 }
1727 
onSearch()1728 void HTMLInputElement::onSearch()
1729 {
1730     ASSERT(isSearchField());
1731     if (renderer())
1732         toRenderTextControlSingleLine(renderer())->stopSearchEventTimer();
1733     dispatchEvent(eventNames().searchEvent, true, false);
1734 }
1735 
selection() const1736 VisibleSelection HTMLInputElement::selection() const
1737 {
1738     if (!renderer() || !isTextField() || m_data.cachedSelectionStart() == -1 || m_data.cachedSelectionEnd() == -1)
1739         return VisibleSelection();
1740     return toRenderTextControl(renderer())->selection(m_data.cachedSelectionStart(), m_data.cachedSelectionEnd());
1741 }
1742 
documentDidBecomeActive()1743 void HTMLInputElement::documentDidBecomeActive()
1744 {
1745     ASSERT(needsActivationCallback());
1746     reset();
1747 }
1748 
willMoveToNewOwnerDocument()1749 void HTMLInputElement::willMoveToNewOwnerDocument()
1750 {
1751     // Always unregister for cache callbacks when leaving a document, even if we would otherwise like to be registered
1752     if (needsActivationCallback())
1753         document()->unregisterForDocumentActivationCallbacks(this);
1754 
1755     document()->checkedRadioButtons().removeButton(this);
1756 
1757     HTMLFormControlElementWithState::willMoveToNewOwnerDocument();
1758 }
1759 
didMoveToNewOwnerDocument()1760 void HTMLInputElement::didMoveToNewOwnerDocument()
1761 {
1762     registerForActivationCallbackIfNeeded();
1763 
1764     HTMLFormControlElementWithState::didMoveToNewOwnerDocument();
1765 }
1766 
addSubresourceAttributeURLs(ListHashSet<KURL> & urls) const1767 void HTMLInputElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
1768 {
1769     HTMLFormControlElementWithState::addSubresourceAttributeURLs(urls);
1770 
1771     addSubresourceURL(urls, src());
1772 }
1773 
willValidate() const1774 bool HTMLInputElement::willValidate() const
1775 {
1776     // FIXME: This shall check for new WF2 input types too
1777     return HTMLFormControlElementWithState::willValidate() && inputType() != HIDDEN &&
1778            inputType() != BUTTON && inputType() != RESET;
1779 }
1780 
placeholderShouldBeVisible() const1781 bool HTMLInputElement::placeholderShouldBeVisible() const
1782 {
1783     return m_data.placeholderShouldBeVisible();
1784 }
1785 
1786 } // namespace
1787