HTMLFormControlElement.cpp revision 2bde8e466a4451c7319e3a072d118917957d6554
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
123void HTMLFormControlElement::attach()
124{
125    ASSERT(!attached());
126
127    HTMLElement::attach();
128
129    // The call to updateFromElement() needs to go after the call through
130    // to the base class's attach() because that can sometimes do a close
131    // on the renderer.
132    if (renderer())
133        renderer()->updateFromElement();
134
135    // Focus the element if it should honour its autofocus attribute.
136    // We have to determine if the element is a TextArea/Input/Button/Select,
137    // if input type hidden ignore autofocus. So if disabled or readonly.
138    bool isInputTypeHidden = false;
139    if (hasTagName(inputTag))
140        isInputTypeHidden = static_cast<HTMLInputElement*>(this)->isInputTypeHidden();
141
142    if (autofocus() && renderer() && !document()->ignoreAutofocus() && !isReadOnlyFormControl() &&
143            ((hasTagName(inputTag) && !isInputTypeHidden) || hasTagName(selectTag) ||
144              hasTagName(keygenTag) || hasTagName(buttonTag) || hasTagName(textareaTag)))
145         focus();
146}
147
148void HTMLFormControlElement::willMoveToNewOwnerDocument()
149{
150    FormAssociatedElement::willMoveToNewOwnerDocument();
151    HTMLElement::willMoveToNewOwnerDocument();
152}
153
154void HTMLFormControlElement::insertedIntoTree(bool deep)
155{
156    FormAssociatedElement::insertedIntoTree();
157    if (!form())
158        document()->checkedRadioButtons().addButton(this);
159
160    HTMLElement::insertedIntoTree(deep);
161}
162
163void HTMLFormControlElement::removedFromTree(bool deep)
164{
165    FormAssociatedElement::removedFromTree();
166    HTMLElement::removedFromTree(deep);
167}
168
169void HTMLFormControlElement::insertedIntoDocument()
170{
171    HTMLElement::insertedIntoDocument();
172    FormAssociatedElement::insertedIntoDocument();
173}
174
175void HTMLFormControlElement::removedFromDocument()
176{
177    HTMLElement::removedFromDocument();
178    FormAssociatedElement::removedFromDocument();
179}
180
181const AtomicString& HTMLFormControlElement::formControlName() const
182{
183    const AtomicString& name = fastGetAttribute(nameAttr);
184    return name.isNull() ? emptyAtom : name;
185}
186
187void HTMLFormControlElement::setName(const AtomicString& value)
188{
189    setAttribute(nameAttr, value);
190}
191
192bool HTMLFormControlElement::wasChangedSinceLastFormControlChangeEvent() const
193{
194    return m_wasChangedSinceLastFormControlChangeEvent;
195}
196
197void HTMLFormControlElement::setChangedSinceLastFormControlChangeEvent(bool changed)
198{
199    m_wasChangedSinceLastFormControlChangeEvent = changed;
200}
201
202void HTMLFormControlElement::dispatchFormControlChangeEvent()
203{
204    HTMLElement::dispatchChangeEvents();
205    setChangedSinceLastFormControlChangeEvent(false);
206}
207
208void HTMLFormControlElement::dispatchFormControlInputEvent()
209{
210    setChangedSinceLastFormControlChangeEvent(true);
211    HTMLElement::dispatchInputEvents();
212}
213
214void HTMLFormControlElement::setDisabled(bool b)
215{
216    setAttribute(disabledAttr, b ? "" : 0);
217}
218
219bool HTMLFormControlElement::autofocus() const
220{
221    return hasAttribute(autofocusAttr);
222}
223
224bool HTMLFormControlElement::required() const
225{
226    return m_required;
227}
228
229static void updateFromElementCallback(Node* node)
230{
231    ASSERT_ARG(node, node->isElementNode());
232    ASSERT_ARG(node, static_cast<Element*>(node)->isFormControlElement());
233    ASSERT(node->renderer());
234    if (RenderObject* renderer = node->renderer())
235        renderer->updateFromElement();
236}
237
238void HTMLFormControlElement::recalcStyle(StyleChange change)
239{
240    HTMLElement::recalcStyle(change);
241
242    // updateFromElement() can cause the selection to change, and in turn
243    // trigger synchronous layout, so it must not be called during style recalc.
244    if (renderer())
245        queuePostAttachCallback(updateFromElementCallback, this);
246}
247
248bool HTMLFormControlElement::supportsFocus() const
249{
250    return !m_disabled;
251}
252
253bool HTMLFormControlElement::isFocusable() const
254{
255    if (!renderer() ||
256        !renderer()->isBox() || toRenderBox(renderer())->size().isEmpty())
257        return false;
258    // HTMLElement::isFocusable handles visibility and calls suportsFocus which
259    // will cover the disabled case.
260    return HTMLElement::isFocusable();
261}
262
263bool HTMLFormControlElement::isKeyboardFocusable(KeyboardEvent* event) const
264{
265    if (isFocusable())
266        if (document()->frame())
267            return document()->frame()->eventHandler()->tabsToAllFormControls(event);
268    return false;
269}
270
271bool HTMLFormControlElement::isMouseFocusable() const
272{
273#if PLATFORM(GTK) || PLATFORM(QT)
274    return HTMLElement::isMouseFocusable();
275#else
276    return false;
277#endif
278}
279
280short HTMLFormControlElement::tabIndex() const
281{
282    // Skip the supportsFocus check in HTMLElement.
283    return Element::tabIndex();
284}
285
286bool HTMLFormControlElement::recalcWillValidate() const
287{
288    // FIXME: Should return false if this element has a datalist element as an
289    // ancestor. See HTML5 4.10.10 'The datalist element.'
290    return !m_disabled && !m_readOnly;
291}
292
293bool HTMLFormControlElement::willValidate() const
294{
295    if (!m_willValidateInitialized) {
296        m_willValidateInitialized = true;
297        m_willValidate = recalcWillValidate();
298    } else {
299        // If the following assertion fails, setNeedsWillValidateCheck() is not
300        // called correctly when something which changes recalcWillValidate() result
301        // is updated.
302        ASSERT(m_willValidate == recalcWillValidate());
303    }
304    return m_willValidate;
305}
306
307void HTMLFormControlElement::setNeedsWillValidateCheck()
308{
309    // We need to recalculate willValidte immediately because willValidate
310    // change can causes style change.
311    bool newWillValidate = recalcWillValidate();
312    if (m_willValidateInitialized && m_willValidate == newWillValidate)
313        return;
314    m_willValidateInitialized = true;
315    m_willValidate = newWillValidate;
316    setNeedsStyleRecalc();
317    if (!m_willValidate)
318        hideVisibleValidationMessage();
319}
320
321String HTMLFormControlElement::validationMessage()
322{
323    return validity()->validationMessage();
324}
325
326void HTMLFormControlElement::updateVisibleValidationMessage()
327{
328    Page* page = document()->page();
329    if (!page)
330        return;
331    String message;
332    if (renderer() && willValidate()) {
333        message = validationMessage().stripWhiteSpace();
334        // HTML5 specification doesn't ask UA to show the title attribute value
335        // with the validationMessage.  However, this behavior is same as Opera
336        // and the specification describes such behavior as an example.
337        const AtomicString& title = getAttribute(titleAttr);
338        if (!message.isEmpty() && !title.isEmpty()) {
339            message.append('\n');
340            message.append(title);
341        }
342    }
343    if (message.isEmpty()) {
344        hideVisibleValidationMessage();
345        return;
346    }
347    if (!m_validationMessage) {
348        m_validationMessage = ValidationMessage::create(this);
349        m_validationMessage->setMessage(message);
350    } else {
351        // Call setMessage() even if m_validationMesage->message() == message
352        // because the existing message might be to be hidden.
353        m_validationMessage->setMessage(message);
354    }
355}
356
357void HTMLFormControlElement::hideVisibleValidationMessage()
358{
359    if (m_validationMessage)
360        m_validationMessage->requestToHideMessage();
361}
362
363String HTMLFormControlElement::visibleValidationMessage() const
364{
365    return m_validationMessage ? m_validationMessage->message() : String();
366}
367
368bool HTMLFormControlElement::checkValidity(Vector<RefPtr<FormAssociatedElement> >* unhandledInvalidControls)
369{
370    if (!willValidate() || isValidFormControlElement())
371        return true;
372    // An event handler can deref this object.
373    RefPtr<HTMLFormControlElement> protector(this);
374    RefPtr<Document> originalDocument(document());
375    bool needsDefaultAction = dispatchEvent(Event::create(eventNames().invalidEvent, false, true));
376    if (needsDefaultAction && unhandledInvalidControls && inDocument() && originalDocument == document())
377        unhandledInvalidControls->append(this);
378    return false;
379}
380
381bool HTMLFormControlElement::isValidFormControlElement()
382{
383    // If the following assertion fails, setNeedsValidityCheck() is not called
384    // correctly when something which changes validity is updated.
385    ASSERT(m_isValid == validity()->valid());
386    return m_isValid;
387}
388
389void HTMLFormControlElement::setNeedsValidityCheck()
390{
391    bool newIsValid = validity()->valid();
392    if (willValidate() && newIsValid != m_isValid) {
393        // Update style for pseudo classes such as :valid :invalid.
394        setNeedsStyleRecalc();
395    }
396    m_isValid = newIsValid;
397
398    // Updates only if this control already has a validtion message.
399    if (!visibleValidationMessage().isEmpty()) {
400        // Calls updateVisibleValidationMessage() even if m_isValid is not
401        // changed because a validation message can be chagned.
402        updateVisibleValidationMessage();
403    }
404}
405
406void HTMLFormControlElement::setCustomValidity(const String& error)
407{
408    validity()->setCustomErrorMessage(error);
409}
410
411void HTMLFormControlElement::dispatchFocusEvent()
412{
413    if (document()->page())
414        document()->page()->chrome()->client()->formDidFocus(this);
415
416    HTMLElement::dispatchFocusEvent();
417}
418
419void HTMLFormControlElement::dispatchBlurEvent()
420{
421    if (document()->page())
422        document()->page()->chrome()->client()->formDidBlur(this);
423
424    HTMLElement::dispatchBlurEvent();
425    hideVisibleValidationMessage();
426}
427
428HTMLFormElement* HTMLFormControlElement::virtualForm() const
429{
430    return FormAssociatedElement::form();
431}
432
433bool HTMLFormControlElement::isDefaultButtonForForm() const
434{
435    return isSuccessfulSubmitButton() && form() && form()->defaultButton() == this;
436}
437
438void HTMLFormControlElement::attributeChanged(Attribute* attr, bool preserveDecls)
439{
440    if (attr->name() == formAttr) {
441        formAttributeChanged();
442        if (!form())
443            document()->checkedRadioButtons().addButton(this);
444    } else
445        HTMLElement::attributeChanged(attr, preserveDecls);
446}
447
448bool HTMLFormControlElement::isLabelable() const
449{
450    // FIXME: Add meterTag and outputTag to the list once we support them.
451    return hasTagName(buttonTag) || hasTagName(inputTag) || hasTagName(keygenTag)
452#if ENABLE(METER_TAG)
453        || hasTagName(meterTag)
454#endif
455#if ENABLE(PROGRESS_TAG)
456        || hasTagName(progressTag)
457#endif
458        || hasTagName(selectTag) || hasTagName(textareaTag);
459}
460
461PassRefPtr<NodeList> HTMLFormControlElement::labels()
462{
463    if (!isLabelable())
464        return 0;
465    if (!document())
466        return 0;
467
468    NodeRareData* data = Node::ensureRareData();
469    if (!data->nodeLists()) {
470        data->setNodeLists(NodeListsNodeData::create());
471        document()->addNodeListCache();
472    }
473
474    return LabelsNodeList::create(this);
475}
476
477HTMLFormControlElementWithState::HTMLFormControlElementWithState(const QualifiedName& tagName, Document* doc, HTMLFormElement* f)
478    : HTMLFormControlElement(tagName, doc, f)
479{
480    document()->registerFormElementWithState(this);
481}
482
483HTMLFormControlElementWithState::~HTMLFormControlElementWithState()
484{
485    document()->unregisterFormElementWithState(this);
486}
487
488void HTMLFormControlElementWithState::willMoveToNewOwnerDocument()
489{
490    document()->unregisterFormElementWithState(this);
491    HTMLFormControlElement::willMoveToNewOwnerDocument();
492}
493
494void HTMLFormControlElementWithState::didMoveToNewOwnerDocument()
495{
496    document()->registerFormElementWithState(this);
497    HTMLFormControlElement::didMoveToNewOwnerDocument();
498}
499
500bool HTMLFormControlElementWithState::autoComplete() const
501{
502    if (!form())
503        return true;
504    return form()->autoComplete();
505}
506
507bool HTMLFormControlElementWithState::shouldSaveAndRestoreFormControlState() const
508{
509    // We don't save/restore control state in a form with autocomplete=off.
510    return attached() && autoComplete();
511}
512
513void HTMLFormControlElementWithState::finishParsingChildren()
514{
515    HTMLFormControlElement::finishParsingChildren();
516
517    // We don't save state of a control with shouldSaveAndRestoreFormControlState()=false.
518    // But we need to skip restoring process too because a control in another
519    // form might have the same pair of name and type and saved its state.
520    if (!shouldSaveAndRestoreFormControlState())
521        return;
522
523    Document* doc = document();
524    if (doc->hasStateForNewFormElements()) {
525        String state;
526        if (doc->takeStateForFormElement(name().impl(), type().impl(), state))
527            restoreFormControlState(state);
528    }
529}
530
531void HTMLFormControlElementWithState::defaultEventHandler(Event* event)
532{
533    if (event->type() == eventNames().webkitEditableContentChangedEvent && renderer() && renderer()->isTextControl()) {
534        toRenderTextControl(renderer())->subtreeHasChanged();
535        return;
536    }
537
538    HTMLFormControlElement::defaultEventHandler(event);
539}
540
541HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* form)
542    : HTMLFormControlElementWithState(tagName, doc, form)
543{
544}
545
546HTMLTextFormControlElement::~HTMLTextFormControlElement()
547{
548}
549
550void HTMLTextFormControlElement::insertedIntoDocument()
551{
552    HTMLFormControlElement::insertedIntoDocument();
553    setTextAsOfLastFormControlChangeEvent(value());
554}
555
556void HTMLTextFormControlElement::dispatchFocusEvent()
557{
558    if (supportsPlaceholder())
559        updatePlaceholderVisibility(false);
560    handleFocusEvent();
561    HTMLFormControlElementWithState::dispatchFocusEvent();
562}
563
564void HTMLTextFormControlElement::dispatchBlurEvent()
565{
566    if (supportsPlaceholder())
567        updatePlaceholderVisibility(false);
568    handleBlurEvent();
569    HTMLFormControlElementWithState::dispatchBlurEvent();
570}
571
572String HTMLTextFormControlElement::strippedPlaceholder() const
573{
574    // According to the HTML5 specification, we need to remove CR and LF from
575    // the attribute value.
576    const AtomicString& attributeValue = getAttribute(placeholderAttr);
577    if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn))
578        return attributeValue;
579
580    Vector<UChar> stripped;
581    unsigned length = attributeValue.length();
582    stripped.reserveCapacity(length);
583    for (unsigned i = 0; i < length; ++i) {
584        UChar character = attributeValue[i];
585        if (character == newlineCharacter || character == carriageReturn)
586            continue;
587        stripped.append(character);
588    }
589    return String::adopt(stripped);
590}
591
592static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; }
593
594bool HTMLTextFormControlElement::isPlaceholderEmpty() const
595{
596    const AtomicString& attributeValue = getAttribute(placeholderAttr);
597    return attributeValue.string().find(isNotLineBreak) == notFound;
598}
599
600bool HTMLTextFormControlElement::placeholderShouldBeVisible() const
601{
602    return supportsPlaceholder()
603        && isEmptyValue()
604        && isEmptySuggestedValue()
605        && !isPlaceholderEmpty()
606        && (document()->focusedNode() != this || (renderer() && renderer()->theme()->shouldShowPlaceholderWhenFocused()));
607}
608
609void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged)
610{
611    if (supportsPlaceholder() && renderer())
612        toRenderTextControl(renderer())->updatePlaceholderVisibility(placeholderShouldBeVisible(), placeholderValueChanged);
613}
614
615RenderTextControl* HTMLTextFormControlElement::textRendererAfterUpdateLayout()
616{
617    if (!isTextFormControl())
618        return 0;
619    document()->updateLayoutIgnorePendingStylesheets();
620    return toRenderTextControl(renderer());
621}
622
623void HTMLTextFormControlElement::setSelectionStart(int start)
624{
625    setSelectionRange(start, max(start, selectionEnd()));
626}
627
628void HTMLTextFormControlElement::setSelectionEnd(int end)
629{
630    setSelectionRange(min(end, selectionStart()), end);
631}
632
633void HTMLTextFormControlElement::select()
634{
635    setSelectionRange(0, numeric_limits<int>::max());
636}
637
638void HTMLTextFormControlElement::dispatchFormControlChangeEvent()
639{
640    if (m_textAsOfLastFormControlChangeEvent != value()) {
641        HTMLElement::dispatchChangeEvents();
642        setTextAsOfLastFormControlChangeEvent(value());
643    }
644    setChangedSinceLastFormControlChangeEvent(false);
645}
646
647void HTMLTextFormControlElement::setSelectionRange(int start, int end)
648{
649    WebCore::setSelectionRange(this, start, end);
650}
651
652int HTMLTextFormControlElement::selectionStart() const
653{
654    if (!isTextFormControl())
655        return 0;
656    if (document()->focusedNode() != this && cachedSelectionStart() >= 0)
657        return cachedSelectionStart();
658    if (!renderer())
659        return 0;
660    return toRenderTextControl(renderer())->selectionStart();
661}
662
663int HTMLTextFormControlElement::selectionEnd() const
664{
665    if (!isTextFormControl())
666        return 0;
667    if (document()->focusedNode() != this && cachedSelectionEnd() >= 0)
668        return cachedSelectionEnd();
669    if (!renderer())
670        return 0;
671    return toRenderTextControl(renderer())->selectionEnd();
672}
673
674PassRefPtr<Range> HTMLTextFormControlElement::selection() const
675{
676    if (!renderer() || !isTextFormControl() || cachedSelectionStart() < 0 || cachedSelectionEnd() < 0)
677        return 0;
678    return toRenderTextControl(renderer())->selection(cachedSelectionStart(), cachedSelectionEnd());
679}
680
681void HTMLTextFormControlElement::parseMappedAttribute(Attribute* attr)
682{
683    if (attr->name() == placeholderAttr)
684        updatePlaceholderVisibility(true);
685    else if (attr->name() == onselectAttr)
686        setAttributeEventListener(eventNames().selectEvent, createAttributeEventListener(this, attr));
687    else if (attr->name() == onchangeAttr)
688        setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr));
689    else
690        HTMLFormControlElementWithState::parseMappedAttribute(attr);
691}
692
693} // namespace Webcore
694