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