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