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 Apple Inc. All rights reserved.
6 *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "HTMLFormControlElement.h"
27
28#include "Attribute.h"
29#include "Chrome.h"
30#include "ChromeClient.h"
31#include "Document.h"
32#include "DocumentParser.h"
33#include "ElementRareData.h"
34#include "Event.h"
35#include "EventHandler.h"
36#include "EventNames.h"
37#include "Frame.h"
38#include "HTMLFormElement.h"
39#include "HTMLInputElement.h"
40#include "HTMLNames.h"
41#include "LabelsNodeList.h"
42#include "Page.h"
43#include "RenderBox.h"
44#include "RenderTextControl.h"
45#include "RenderTheme.h"
46#include "ScriptEventListener.h"
47#include "ValidationMessage.h"
48#include "ValidityState.h"
49#include <limits>
50#include <wtf/Vector.h>
51#include <wtf/unicode/CharacterNames.h>
52
53namespace WebCore {
54
55using namespace HTMLNames;
56using namespace std;
57
58HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
59    : HTMLElement(tagName, document)
60    , FormAssociatedElement(form)
61    , m_disabled(false)
62    , m_readOnly(false)
63    , m_required(false)
64    , m_valueMatchesRenderer(false)
65    , m_willValidateInitialized(false)
66    , m_willValidate(true)
67    , m_isValid(true)
68    , m_wasChangedSinceLastFormControlChangeEvent(false)
69{
70    if (!this->form())
71        setForm(findFormAncestor());
72    if (this->form())
73        this->form()->registerFormElement(this);
74}
75
76HTMLFormControlElement::~HTMLFormControlElement()
77{
78    if (form())
79        form()->removeFormElement(this);
80}
81
82void HTMLFormControlElement::detach()
83{
84    m_validationMessage = 0;
85    HTMLElement::detach();
86}
87
88bool HTMLFormControlElement::formNoValidate() const
89{
90    return fastHasAttribute(formnovalidateAttr);
91}
92
93void HTMLFormControlElement::parseMappedAttribute(Attribute* attr)
94{
95    if (attr->name() == disabledAttr) {
96        bool oldDisabled = m_disabled;
97        m_disabled = !attr->isNull();
98        if (oldDisabled != m_disabled) {
99            setNeedsStyleRecalc();
100            if (renderer() && renderer()->style()->hasAppearance())
101                renderer()->theme()->stateChanged(renderer(), EnabledState);
102        }
103    } else if (attr->name() == readonlyAttr) {
104        bool oldReadOnly = m_readOnly;
105        m_readOnly = !attr->isNull();
106        if (oldReadOnly != m_readOnly) {
107            setNeedsStyleRecalc();
108            if (renderer() && renderer()->style()->hasAppearance())
109                renderer()->theme()->stateChanged(renderer(), ReadOnlyState);
110        }
111    } else if (attr->name() == requiredAttr) {
112        bool oldRequired = m_required;
113        m_required = !attr->isNull();
114        if (oldRequired != m_required) {
115            setNeedsValidityCheck();
116            setNeedsStyleRecalc(); // Updates for :required :optional classes.
117        }
118    } else
119        HTMLElement::parseMappedAttribute(attr);
120    setNeedsWillValidateCheck();
121}
122
123static bool shouldAutofocus(HTMLFormControlElement* element)
124{
125    if (!element->autofocus())
126        return false;
127    if (!element->renderer())
128        return false;
129    if (element->document()->ignoreAutofocus())
130        return false;
131    if (element->isReadOnlyFormControl())
132        return false;
133
134    // FIXME: Should this set of hasTagName checks be replaced by a
135    // virtual member function?
136    if (element->hasTagName(inputTag))
137        return !static_cast<HTMLInputElement*>(element)->isInputTypeHidden();
138    if (element->hasTagName(selectTag))
139        return true;
140    if (element->hasTagName(keygenTag))
141        return true;
142    if (element->hasTagName(buttonTag))
143        return true;
144    if (element->hasTagName(textareaTag))
145        return true;
146
147    return false;
148}
149
150static void focusPostAttach(Node* element)
151{
152    static_cast<Element*>(element)->focus();
153    element->deref();
154}
155
156void HTMLFormControlElement::attach()
157{
158    ASSERT(!attached());
159
160    suspendPostAttachCallbacks();
161
162    HTMLElement::attach();
163
164    // The call to updateFromElement() needs to go after the call through
165    // to the base class's attach() because that can sometimes do a close
166    // on the renderer.
167    if (renderer())
168        renderer()->updateFromElement();
169
170    if (shouldAutofocus(this)) {
171        ref();
172        queuePostAttachCallback(focusPostAttach, this);
173    }
174
175    resumePostAttachCallbacks();
176}
177
178void HTMLFormControlElement::willMoveToNewOwnerDocument()
179{
180    FormAssociatedElement::willMoveToNewOwnerDocument();
181    HTMLElement::willMoveToNewOwnerDocument();
182}
183
184void HTMLFormControlElement::insertedIntoTree(bool deep)
185{
186    FormAssociatedElement::insertedIntoTree();
187    if (!form())
188        document()->checkedRadioButtons().addButton(this);
189
190    HTMLElement::insertedIntoTree(deep);
191}
192
193void HTMLFormControlElement::removedFromTree(bool deep)
194{
195    FormAssociatedElement::removedFromTree();
196    HTMLElement::removedFromTree(deep);
197}
198
199void HTMLFormControlElement::insertedIntoDocument()
200{
201    HTMLElement::insertedIntoDocument();
202    FormAssociatedElement::insertedIntoDocument();
203}
204
205void HTMLFormControlElement::removedFromDocument()
206{
207    HTMLElement::removedFromDocument();
208    FormAssociatedElement::removedFromDocument();
209}
210
211const AtomicString& HTMLFormControlElement::formControlName() const
212{
213    const AtomicString& name = fastGetAttribute(nameAttr);
214    return name.isNull() ? emptyAtom : name;
215}
216
217void HTMLFormControlElement::setName(const AtomicString& value)
218{
219    setAttribute(nameAttr, value);
220}
221
222bool HTMLFormControlElement::wasChangedSinceLastFormControlChangeEvent() const
223{
224    return m_wasChangedSinceLastFormControlChangeEvent;
225}
226
227void HTMLFormControlElement::setChangedSinceLastFormControlChangeEvent(bool changed)
228{
229    m_wasChangedSinceLastFormControlChangeEvent = changed;
230}
231
232void HTMLFormControlElement::dispatchFormControlChangeEvent()
233{
234    HTMLElement::dispatchChangeEvent();
235    setChangedSinceLastFormControlChangeEvent(false);
236}
237
238void HTMLFormControlElement::dispatchFormControlInputEvent()
239{
240    setChangedSinceLastFormControlChangeEvent(true);
241    HTMLElement::dispatchInputEvent();
242}
243
244void HTMLFormControlElement::setDisabled(bool b)
245{
246    setAttribute(disabledAttr, b ? "" : 0);
247}
248
249bool HTMLFormControlElement::autofocus() const
250{
251    return hasAttribute(autofocusAttr);
252}
253
254bool HTMLFormControlElement::required() const
255{
256    return m_required;
257}
258
259static void updateFromElementCallback(Node* node)
260{
261    ASSERT_ARG(node, node->isElementNode());
262    ASSERT_ARG(node, static_cast<Element*>(node)->isFormControlElement());
263    ASSERT(node->renderer());
264    if (RenderObject* renderer = node->renderer())
265        renderer->updateFromElement();
266}
267
268void HTMLFormControlElement::recalcStyle(StyleChange change)
269{
270    HTMLElement::recalcStyle(change);
271
272    // updateFromElement() can cause the selection to change, and in turn
273    // trigger synchronous layout, so it must not be called during style recalc.
274    if (renderer())
275        queuePostAttachCallback(updateFromElementCallback, this);
276}
277
278bool HTMLFormControlElement::supportsFocus() const
279{
280    return !m_disabled;
281}
282
283bool HTMLFormControlElement::isFocusable() const
284{
285    if (!renderer() ||
286        !renderer()->isBox() || toRenderBox(renderer())->size().isEmpty())
287        return false;
288    // HTMLElement::isFocusable handles visibility and calls suportsFocus which
289    // will cover the disabled case.
290    return HTMLElement::isFocusable();
291}
292
293bool HTMLFormControlElement::isKeyboardFocusable(KeyboardEvent* event) const
294{
295    if (isFocusable())
296        if (document()->frame())
297            return document()->frame()->eventHandler()->tabsToAllFormControls(event);
298    return false;
299}
300
301bool HTMLFormControlElement::isMouseFocusable() const
302{
303#if PLATFORM(GTK) || PLATFORM(QT)
304    return HTMLElement::isMouseFocusable();
305#else
306    return false;
307#endif
308}
309
310short HTMLFormControlElement::tabIndex() const
311{
312    // Skip the supportsFocus check in HTMLElement.
313    return Element::tabIndex();
314}
315
316bool HTMLFormControlElement::recalcWillValidate() const
317{
318    // FIXME: Should return false if this element has a datalist element as an
319    // ancestor. See HTML5 4.10.10 'The datalist element.'
320    return !m_disabled && !m_readOnly;
321}
322
323bool HTMLFormControlElement::willValidate() const
324{
325    if (!m_willValidateInitialized) {
326        m_willValidateInitialized = true;
327        m_willValidate = recalcWillValidate();
328    } else {
329        // If the following assertion fails, setNeedsWillValidateCheck() is not
330        // called correctly when something which changes recalcWillValidate() result
331        // is updated.
332        ASSERT(m_willValidate == recalcWillValidate());
333    }
334    return m_willValidate;
335}
336
337void HTMLFormControlElement::setNeedsWillValidateCheck()
338{
339    // We need to recalculate willValidte immediately because willValidate
340    // change can causes style change.
341    bool newWillValidate = recalcWillValidate();
342    if (m_willValidateInitialized && m_willValidate == newWillValidate)
343        return;
344    m_willValidateInitialized = true;
345    m_willValidate = newWillValidate;
346    setNeedsStyleRecalc();
347    if (!m_willValidate)
348        hideVisibleValidationMessage();
349}
350
351String HTMLFormControlElement::validationMessage()
352{
353    return validity()->validationMessage();
354}
355
356void HTMLFormControlElement::updateVisibleValidationMessage()
357{
358    Page* page = document()->page();
359    if (!page)
360        return;
361    String message;
362    if (renderer() && willValidate()) {
363        message = validationMessage().stripWhiteSpace();
364        // HTML5 specification doesn't ask UA to show the title attribute value
365        // with the validationMessage.  However, this behavior is same as Opera
366        // and the specification describes such behavior as an example.
367        const AtomicString& title = getAttribute(titleAttr);
368        if (!message.isEmpty() && !title.isEmpty()) {
369            message.append('\n');
370            message.append(title);
371        }
372    }
373    if (message.isEmpty()) {
374        hideVisibleValidationMessage();
375        return;
376    }
377    if (!m_validationMessage) {
378        m_validationMessage = ValidationMessage::create(this);
379        m_validationMessage->setMessage(message);
380    } else {
381        // Call setMessage() even if m_validationMesage->message() == message
382        // because the existing message might be to be hidden.
383        m_validationMessage->setMessage(message);
384    }
385}
386
387void HTMLFormControlElement::hideVisibleValidationMessage()
388{
389    if (m_validationMessage)
390        m_validationMessage->requestToHideMessage();
391}
392
393String HTMLFormControlElement::visibleValidationMessage() const
394{
395    return m_validationMessage ? m_validationMessage->message() : String();
396}
397
398bool HTMLFormControlElement::checkValidity(Vector<RefPtr<FormAssociatedElement> >* unhandledInvalidControls)
399{
400    if (!willValidate() || isValidFormControlElement())
401        return true;
402    // An event handler can deref this object.
403    RefPtr<HTMLFormControlElement> protector(this);
404    RefPtr<Document> originalDocument(document());
405    bool needsDefaultAction = dispatchEvent(Event::create(eventNames().invalidEvent, false, true));
406    if (needsDefaultAction && unhandledInvalidControls && inDocument() && originalDocument == document())
407        unhandledInvalidControls->append(this);
408    return false;
409}
410
411bool HTMLFormControlElement::isValidFormControlElement()
412{
413    // If the following assertion fails, setNeedsValidityCheck() is not called
414    // correctly when something which changes validity is updated.
415    ASSERT(m_isValid == validity()->valid());
416    return m_isValid;
417}
418
419void HTMLFormControlElement::setNeedsValidityCheck()
420{
421    bool newIsValid = validity()->valid();
422    if (willValidate() && newIsValid != m_isValid) {
423        // Update style for pseudo classes such as :valid :invalid.
424        setNeedsStyleRecalc();
425    }
426    m_isValid = newIsValid;
427
428    // Updates only if this control already has a validtion message.
429    if (!visibleValidationMessage().isEmpty()) {
430        // Calls updateVisibleValidationMessage() even if m_isValid is not
431        // changed because a validation message can be chagned.
432        updateVisibleValidationMessage();
433    }
434}
435
436void HTMLFormControlElement::setCustomValidity(const String& error)
437{
438    validity()->setCustomErrorMessage(error);
439}
440
441void HTMLFormControlElement::dispatchFocusEvent()
442{
443    if (document()->page())
444        document()->page()->chrome()->client()->formDidFocus(this);
445
446    HTMLElement::dispatchFocusEvent();
447}
448
449void HTMLFormControlElement::dispatchBlurEvent()
450{
451    if (document()->page())
452        document()->page()->chrome()->client()->formDidBlur(this);
453
454    HTMLElement::dispatchBlurEvent();
455    hideVisibleValidationMessage();
456}
457
458HTMLFormElement* HTMLFormControlElement::virtualForm() const
459{
460    return FormAssociatedElement::form();
461}
462
463bool HTMLFormControlElement::isDefaultButtonForForm() const
464{
465    return isSuccessfulSubmitButton() && form() && form()->defaultButton() == this;
466}
467
468void HTMLFormControlElement::attributeChanged(Attribute* attr, bool preserveDecls)
469{
470    if (attr->name() == formAttr) {
471        formAttributeChanged();
472        if (!form())
473            document()->checkedRadioButtons().addButton(this);
474    } else
475        HTMLElement::attributeChanged(attr, preserveDecls);
476}
477
478bool HTMLFormControlElement::isLabelable() const
479{
480    // FIXME: Add meterTag and outputTag to the list once we support them.
481    return hasTagName(buttonTag) || hasTagName(inputTag) || hasTagName(keygenTag)
482#if ENABLE(METER_TAG)
483        || hasTagName(meterTag)
484#endif
485#if ENABLE(PROGRESS_TAG)
486        || hasTagName(progressTag)
487#endif
488        || hasTagName(selectTag) || hasTagName(textareaTag);
489}
490
491PassRefPtr<NodeList> HTMLFormControlElement::labels()
492{
493    if (!isLabelable())
494        return 0;
495    if (!document())
496        return 0;
497
498    NodeRareData* data = Node::ensureRareData();
499    if (!data->nodeLists()) {
500        data->setNodeLists(NodeListsNodeData::create());
501        document()->addNodeListCache();
502    }
503
504    return LabelsNodeList::create(this);
505}
506
507HTMLFormControlElementWithState::HTMLFormControlElementWithState(const QualifiedName& tagName, Document* doc, HTMLFormElement* f)
508    : HTMLFormControlElement(tagName, doc, f)
509{
510    document()->registerFormElementWithState(this);
511}
512
513HTMLFormControlElementWithState::~HTMLFormControlElementWithState()
514{
515    document()->unregisterFormElementWithState(this);
516}
517
518void HTMLFormControlElementWithState::willMoveToNewOwnerDocument()
519{
520    document()->unregisterFormElementWithState(this);
521    HTMLFormControlElement::willMoveToNewOwnerDocument();
522}
523
524void HTMLFormControlElementWithState::didMoveToNewOwnerDocument()
525{
526    document()->registerFormElementWithState(this);
527    HTMLFormControlElement::didMoveToNewOwnerDocument();
528}
529
530bool HTMLFormControlElementWithState::autoComplete() const
531{
532    if (!form())
533        return true;
534    return form()->autoComplete();
535}
536
537bool HTMLFormControlElementWithState::shouldSaveAndRestoreFormControlState() const
538{
539    // We don't save/restore control state in a form with autocomplete=off.
540    return attached() && autoComplete();
541}
542
543void HTMLFormControlElementWithState::finishParsingChildren()
544{
545    HTMLFormControlElement::finishParsingChildren();
546
547    // We don't save state of a control with shouldSaveAndRestoreFormControlState()=false.
548    // But we need to skip restoring process too because a control in another
549    // form might have the same pair of name and type and saved its state.
550    if (!shouldSaveAndRestoreFormControlState())
551        return;
552
553    Document* doc = document();
554    if (doc->hasStateForNewFormElements()) {
555        String state;
556        if (doc->takeStateForFormElement(name().impl(), type().impl(), state))
557            restoreFormControlState(state);
558    }
559}
560
561void HTMLFormControlElementWithState::defaultEventHandler(Event* event)
562{
563    if (event->type() == eventNames().webkitEditableContentChangedEvent && renderer() && renderer()->isTextControl()) {
564        toRenderTextControl(renderer())->subtreeHasChanged();
565        return;
566    }
567
568    HTMLFormControlElement::defaultEventHandler(event);
569}
570
571HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* form)
572    : HTMLFormControlElementWithState(tagName, doc, form)
573{
574}
575
576HTMLTextFormControlElement::~HTMLTextFormControlElement()
577{
578}
579
580void HTMLTextFormControlElement::insertedIntoDocument()
581{
582    HTMLFormControlElement::insertedIntoDocument();
583    setTextAsOfLastFormControlChangeEvent(value());
584}
585
586void HTMLTextFormControlElement::dispatchFocusEvent()
587{
588    if (supportsPlaceholder())
589        updatePlaceholderVisibility(false);
590    handleFocusEvent();
591    HTMLFormControlElementWithState::dispatchFocusEvent();
592}
593
594void HTMLTextFormControlElement::dispatchBlurEvent()
595{
596    if (supportsPlaceholder())
597        updatePlaceholderVisibility(false);
598    handleBlurEvent();
599    HTMLFormControlElementWithState::dispatchBlurEvent();
600}
601
602String HTMLTextFormControlElement::strippedPlaceholder() const
603{
604    // According to the HTML5 specification, we need to remove CR and LF from
605    // the attribute value.
606    const AtomicString& attributeValue = getAttribute(placeholderAttr);
607    if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn))
608        return attributeValue;
609
610    Vector<UChar> stripped;
611    unsigned length = attributeValue.length();
612    stripped.reserveCapacity(length);
613    for (unsigned i = 0; i < length; ++i) {
614        UChar character = attributeValue[i];
615        if (character == newlineCharacter || character == carriageReturn)
616            continue;
617        stripped.append(character);
618    }
619    return String::adopt(stripped);
620}
621
622static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; }
623
624bool HTMLTextFormControlElement::isPlaceholderEmpty() const
625{
626    const AtomicString& attributeValue = getAttribute(placeholderAttr);
627    return attributeValue.string().find(isNotLineBreak) == notFound;
628}
629
630bool HTMLTextFormControlElement::placeholderShouldBeVisible() const
631{
632    return supportsPlaceholder()
633        && isEmptyValue()
634        && isEmptySuggestedValue()
635        && !isPlaceholderEmpty()
636        && (document()->focusedNode() != this || (renderer() && renderer()->theme()->shouldShowPlaceholderWhenFocused()));
637}
638
639void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged)
640{
641    if (supportsPlaceholder() && renderer())
642        toRenderTextControl(renderer())->updatePlaceholderVisibility(placeholderShouldBeVisible(), placeholderValueChanged);
643}
644
645RenderTextControl* HTMLTextFormControlElement::textRendererAfterUpdateLayout()
646{
647    if (!isTextFormControl())
648        return 0;
649    document()->updateLayoutIgnorePendingStylesheets();
650    return toRenderTextControl(renderer());
651}
652
653void HTMLTextFormControlElement::setSelectionStart(int start)
654{
655    setSelectionRange(start, max(start, selectionEnd()));
656}
657
658void HTMLTextFormControlElement::setSelectionEnd(int end)
659{
660    setSelectionRange(min(end, selectionStart()), end);
661}
662
663void HTMLTextFormControlElement::select()
664{
665    setSelectionRange(0, numeric_limits<int>::max());
666}
667
668void HTMLTextFormControlElement::dispatchFormControlChangeEvent()
669{
670    if (m_textAsOfLastFormControlChangeEvent != value()) {
671        HTMLElement::dispatchChangeEvent();
672        setTextAsOfLastFormControlChangeEvent(value());
673    }
674    setChangedSinceLastFormControlChangeEvent(false);
675}
676
677void HTMLTextFormControlElement::setSelectionRange(int start, int end)
678{
679    WebCore::setSelectionRange(this, start, end);
680}
681
682int HTMLTextFormControlElement::selectionStart() const
683{
684    if (!isTextFormControl())
685        return 0;
686    if (document()->focusedNode() != this && cachedSelectionStart() >= 0)
687        return cachedSelectionStart();
688    if (!renderer())
689        return 0;
690    return toRenderTextControl(renderer())->selectionStart();
691}
692
693int HTMLTextFormControlElement::selectionEnd() const
694{
695    if (!isTextFormControl())
696        return 0;
697    if (document()->focusedNode() != this && cachedSelectionEnd() >= 0)
698        return cachedSelectionEnd();
699    if (!renderer())
700        return 0;
701    return toRenderTextControl(renderer())->selectionEnd();
702}
703
704PassRefPtr<Range> HTMLTextFormControlElement::selection() const
705{
706    if (!renderer() || !isTextFormControl() || cachedSelectionStart() < 0 || cachedSelectionEnd() < 0)
707        return 0;
708    return toRenderTextControl(renderer())->selection(cachedSelectionStart(), cachedSelectionEnd());
709}
710
711void HTMLTextFormControlElement::parseMappedAttribute(Attribute* attr)
712{
713    if (attr->name() == placeholderAttr)
714        updatePlaceholderVisibility(true);
715    else if (attr->name() == onselectAttr)
716        setAttributeEventListener(eventNames().selectEvent, createAttributeEventListener(this, attr));
717    else if (attr->name() == onchangeAttr)
718        setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr));
719    else
720        HTMLFormControlElementWithState::parseMappedAttribute(attr);
721}
722
723} // namespace Webcore
724