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