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 "core/html/HTMLFormControlElement.h"
27
28#include "core/dom/ElementTraversal.h"
29#include "core/events/Event.h"
30#include "core/frame/UseCounter.h"
31#include "core/html/HTMLDataListElement.h"
32#include "core/html/HTMLFieldSetElement.h"
33#include "core/html/HTMLFormElement.h"
34#include "core/html/HTMLInputElement.h"
35#include "core/html/HTMLLegendElement.h"
36#include "core/html/ValidityState.h"
37#include "core/inspector/ConsoleMessage.h"
38#include "core/page/Page.h"
39#include "core/page/ValidationMessageClient.h"
40#include "core/rendering/RenderBox.h"
41#include "core/rendering/RenderTheme.h"
42#include "platform/text/BidiTextRun.h"
43#include "wtf/Vector.h"
44
45namespace blink {
46
47using namespace HTMLNames;
48
49HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form)
50    : LabelableElement(tagName, document)
51    , m_disabled(false)
52    , m_isAutofilled(false)
53    , m_isReadOnly(false)
54    , m_isRequired(false)
55    , m_hasValidationMessage(false)
56    , m_ancestorDisabledState(AncestorDisabledStateUnknown)
57    , m_dataListAncestorState(Unknown)
58    , m_willValidateInitialized(false)
59    , m_willValidate(true)
60    , m_isValid(true)
61    , m_wasChangedSinceLastFormControlChangeEvent(false)
62    , m_wasFocusedByMouse(false)
63{
64    setHasCustomStyleCallbacks();
65    associateByParser(form);
66}
67
68HTMLFormControlElement::~HTMLFormControlElement()
69{
70#if !ENABLE(OILPAN)
71    setForm(0);
72#endif
73}
74
75void HTMLFormControlElement::trace(Visitor* visitor)
76{
77    FormAssociatedElement::trace(visitor);
78    LabelableElement::trace(visitor);
79}
80
81String HTMLFormControlElement::formEnctype() const
82{
83    const AtomicString& formEnctypeAttr = fastGetAttribute(formenctypeAttr);
84    if (formEnctypeAttr.isNull())
85        return emptyString();
86    return FormSubmission::Attributes::parseEncodingType(formEnctypeAttr);
87}
88
89void HTMLFormControlElement::setFormEnctype(const AtomicString& value)
90{
91    setAttribute(formenctypeAttr, value);
92}
93
94String HTMLFormControlElement::formMethod() const
95{
96    const AtomicString& formMethodAttr = fastGetAttribute(formmethodAttr);
97    if (formMethodAttr.isNull())
98        return emptyString();
99    return FormSubmission::Attributes::methodString(FormSubmission::Attributes::parseMethodType(formMethodAttr));
100}
101
102void HTMLFormControlElement::setFormMethod(const AtomicString& value)
103{
104    setAttribute(formmethodAttr, value);
105}
106
107bool HTMLFormControlElement::formNoValidate() const
108{
109    return fastHasAttribute(formnovalidateAttr);
110}
111
112void HTMLFormControlElement::updateAncestorDisabledState() const
113{
114    HTMLFieldSetElement* fieldSetAncestor = 0;
115    ContainerNode* legendAncestor = 0;
116    for (HTMLElement* ancestor = Traversal<HTMLElement>::firstAncestor(*this); ancestor; ancestor = Traversal<HTMLElement>::firstAncestor(*ancestor)) {
117        if (!legendAncestor && isHTMLLegendElement(*ancestor))
118            legendAncestor = ancestor;
119        if (isHTMLFieldSetElement(*ancestor)) {
120            fieldSetAncestor = toHTMLFieldSetElement(ancestor);
121            break;
122        }
123    }
124    m_ancestorDisabledState = (fieldSetAncestor && fieldSetAncestor->isDisabledFormControl() && !(legendAncestor && legendAncestor == fieldSetAncestor->legend())) ? AncestorDisabledStateDisabled : AncestorDisabledStateEnabled;
125}
126
127void HTMLFormControlElement::ancestorDisabledStateWasChanged()
128{
129    m_ancestorDisabledState = AncestorDisabledStateUnknown;
130    disabledAttributeChanged();
131}
132
133void HTMLFormControlElement::reset()
134{
135    setAutofilled(false);
136    resetImpl();
137}
138
139void HTMLFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
140{
141    if (name == formAttr) {
142        formAttributeChanged();
143        UseCounter::count(document(), UseCounter::FormAttribute);
144    } else if (name == disabledAttr) {
145        bool oldDisabled = m_disabled;
146        m_disabled = !value.isNull();
147        if (oldDisabled != m_disabled)
148            disabledAttributeChanged();
149    } else if (name == readonlyAttr) {
150        bool wasReadOnly = m_isReadOnly;
151        m_isReadOnly = !value.isNull();
152        if (wasReadOnly != m_isReadOnly) {
153            setNeedsWillValidateCheck();
154            setNeedsStyleRecalc(SubtreeStyleChange);
155            if (renderer() && renderer()->style()->hasAppearance())
156                RenderTheme::theme().stateChanged(renderer(), ReadOnlyControlState);
157        }
158    } else if (name == requiredAttr) {
159        bool wasRequired = m_isRequired;
160        m_isRequired = !value.isNull();
161        if (wasRequired != m_isRequired)
162            requiredAttributeChanged();
163        UseCounter::count(document(), UseCounter::RequiredAttribute);
164    } else if (name == autofocusAttr) {
165        HTMLElement::parseAttribute(name, value);
166        UseCounter::count(document(), UseCounter::AutoFocusAttribute);
167    } else
168        HTMLElement::parseAttribute(name, value);
169}
170
171void HTMLFormControlElement::disabledAttributeChanged()
172{
173    setNeedsWillValidateCheck();
174    pseudoStateChanged(CSSSelector::PseudoDisabled);
175    pseudoStateChanged(CSSSelector::PseudoEnabled);
176    if (renderer() && renderer()->style()->hasAppearance())
177        RenderTheme::theme().stateChanged(renderer(), EnabledControlState);
178    if (isDisabledFormControl() && treeScope().adjustedFocusedElement() == this) {
179        // We might want to call blur(), but it's dangerous to dispatch events
180        // here.
181        document().setNeedsFocusedElementCheck();
182    }
183}
184
185void HTMLFormControlElement::requiredAttributeChanged()
186{
187    setNeedsValidityCheck();
188    // Style recalculation is needed because style selectors may include
189    // :required and :optional pseudo-classes.
190    setNeedsStyleRecalc(SubtreeStyleChange);
191}
192
193bool HTMLFormControlElement::supportsAutofocus() const
194{
195    return false;
196}
197
198bool HTMLFormControlElement::isAutofocusable() const
199{
200    return fastHasAttribute(autofocusAttr) && supportsAutofocus();
201}
202
203void HTMLFormControlElement::setAutofilled(bool autofilled)
204{
205    if (autofilled == m_isAutofilled)
206        return;
207
208    m_isAutofilled = autofilled;
209    setNeedsStyleRecalc(SubtreeStyleChange);
210}
211
212static bool shouldAutofocusOnAttach(const HTMLFormControlElement* element)
213{
214    if (!element->isAutofocusable())
215        return false;
216    if (element->document().isSandboxed(SandboxAutomaticFeatures)) {
217        // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
218        element->document().addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, "Blocked autofocusing on a form control because the form's frame is sandboxed and the 'allow-scripts' permission is not set."));
219        return false;
220    }
221
222    return true;
223}
224
225void HTMLFormControlElement::attach(const AttachContext& context)
226{
227    HTMLElement::attach(context);
228
229    if (!renderer())
230        return;
231
232    // The call to updateFromElement() needs to go after the call through
233    // to the base class's attach() because that can sometimes do a close
234    // on the renderer.
235    renderer()->updateFromElement();
236
237    // FIXME: Autofocus handling should be moved to insertedInto according to
238    // the standard.
239    if (shouldAutofocusOnAttach(this))
240        document().setAutofocusElement(this);
241}
242
243void HTMLFormControlElement::didMoveToNewDocument(Document& oldDocument)
244{
245    FormAssociatedElement::didMoveToNewDocument(oldDocument);
246    HTMLElement::didMoveToNewDocument(oldDocument);
247}
248
249Node::InsertionNotificationRequest HTMLFormControlElement::insertedInto(ContainerNode* insertionPoint)
250{
251    m_ancestorDisabledState = AncestorDisabledStateUnknown;
252    m_dataListAncestorState = Unknown;
253    setNeedsWillValidateCheck();
254    HTMLElement::insertedInto(insertionPoint);
255    FormAssociatedElement::insertedInto(insertionPoint);
256    return InsertionDone;
257}
258
259void HTMLFormControlElement::removedFrom(ContainerNode* insertionPoint)
260{
261    hideVisibleValidationMessage();
262    m_hasValidationMessage = false;
263    m_ancestorDisabledState = AncestorDisabledStateUnknown;
264    m_dataListAncestorState = Unknown;
265    HTMLElement::removedFrom(insertionPoint);
266    FormAssociatedElement::removedFrom(insertionPoint);
267}
268
269void HTMLFormControlElement::setChangedSinceLastFormControlChangeEvent(bool changed)
270{
271    m_wasChangedSinceLastFormControlChangeEvent = changed;
272}
273
274void HTMLFormControlElement::dispatchChangeEvent()
275{
276    dispatchScopedEvent(Event::createBubble(EventTypeNames::change));
277}
278
279void HTMLFormControlElement::dispatchFormControlChangeEvent()
280{
281    dispatchChangeEvent();
282    setChangedSinceLastFormControlChangeEvent(false);
283}
284
285void HTMLFormControlElement::dispatchFormControlInputEvent()
286{
287    setChangedSinceLastFormControlChangeEvent(true);
288    HTMLElement::dispatchInputEvent();
289}
290
291HTMLFormElement* HTMLFormControlElement::formOwner() const
292{
293    return FormAssociatedElement::form();
294}
295
296bool HTMLFormControlElement::isDisabledFormControl() const
297{
298    if (m_disabled)
299        return true;
300
301    if (m_ancestorDisabledState == AncestorDisabledStateUnknown)
302        updateAncestorDisabledState();
303    return m_ancestorDisabledState == AncestorDisabledStateDisabled;
304}
305
306bool HTMLFormControlElement::isRequired() const
307{
308    return m_isRequired;
309}
310
311String HTMLFormControlElement::resultForDialogSubmit()
312{
313    return fastGetAttribute(valueAttr);
314}
315
316void HTMLFormControlElement::didRecalcStyle(StyleRecalcChange)
317{
318    if (RenderObject* renderer = this->renderer())
319        renderer->updateFromElement();
320}
321
322bool HTMLFormControlElement::supportsFocus() const
323{
324    return !isDisabledFormControl();
325}
326
327bool HTMLFormControlElement::isKeyboardFocusable() const
328{
329    // Skip tabIndex check in a parent class.
330    return isFocusable();
331}
332
333bool HTMLFormControlElement::shouldShowFocusRingOnMouseFocus() const
334{
335    return false;
336}
337
338bool HTMLFormControlElement::shouldHaveFocusAppearance() const
339{
340    return !m_wasFocusedByMouse || shouldShowFocusRingOnMouseFocus();
341}
342
343void HTMLFormControlElement::dispatchFocusEvent(Element* oldFocusedElement, FocusType type)
344{
345    if (type != FocusTypePage)
346        m_wasFocusedByMouse = type == FocusTypeMouse;
347    HTMLElement::dispatchFocusEvent(oldFocusedElement, type);
348}
349
350void HTMLFormControlElement::willCallDefaultEventHandler(const Event& event)
351{
352    if (!m_wasFocusedByMouse)
353        return;
354    if (!event.isKeyboardEvent() || event.type() != EventTypeNames::keydown)
355        return;
356    m_wasFocusedByMouse = false;
357    if (renderer())
358        renderer()->setShouldDoFullPaintInvalidation(true);
359}
360
361short HTMLFormControlElement::tabIndex() const
362{
363    // Skip the supportsFocus check in HTMLElement.
364    return Element::tabIndex();
365}
366
367bool HTMLFormControlElement::recalcWillValidate() const
368{
369    if (m_dataListAncestorState == Unknown) {
370        if (Traversal<HTMLDataListElement>::firstAncestor(*this))
371            m_dataListAncestorState = InsideDataList;
372        else
373            m_dataListAncestorState = NotInsideDataList;
374    }
375    return m_dataListAncestorState == NotInsideDataList && !isDisabledOrReadOnly();
376}
377
378bool HTMLFormControlElement::willValidate() const
379{
380    if (!m_willValidateInitialized || m_dataListAncestorState == Unknown) {
381        m_willValidateInitialized = true;
382        bool newWillValidate = recalcWillValidate();
383        if (m_willValidate != newWillValidate) {
384            m_willValidate = newWillValidate;
385            const_cast<HTMLFormControlElement*>(this)->setNeedsValidityCheck();
386        }
387    } else {
388        // If the following assertion fails, setNeedsWillValidateCheck() is not
389        // called correctly when something which changes recalcWillValidate() result
390        // is updated.
391        ASSERT(m_willValidate == recalcWillValidate());
392    }
393    return m_willValidate;
394}
395
396void HTMLFormControlElement::setNeedsWillValidateCheck()
397{
398    // We need to recalculate willValidate immediately because willValidate change can causes style change.
399    bool newWillValidate = recalcWillValidate();
400    if (m_willValidateInitialized && m_willValidate == newWillValidate)
401        return;
402    m_willValidateInitialized = true;
403    m_willValidate = newWillValidate;
404    setNeedsValidityCheck();
405    setNeedsStyleRecalc(SubtreeStyleChange);
406    if (!m_willValidate)
407        hideVisibleValidationMessage();
408}
409
410void HTMLFormControlElement::findCustomValidationMessageTextDirection(const String& message, TextDirection &messageDir, String& subMessage, TextDirection &subMessageDir)
411{
412    bool hasStrongDirection;
413    subMessage = fastGetAttribute(titleAttr);
414    messageDir = determineDirectionality(message, hasStrongDirection);
415    if (!subMessage.isEmpty())
416        subMessageDir = renderer()->style()->direction();
417}
418
419void HTMLFormControlElement::updateVisibleValidationMessage()
420{
421    Page* page = document().page();
422    if (!page)
423        return;
424    String message;
425    if (renderer() && willValidate())
426        message = validationMessage().stripWhiteSpace();
427
428    m_hasValidationMessage = true;
429    ValidationMessageClient* client = &page->validationMessageClient();
430    TextDirection messageDir = LTR;
431    TextDirection subMessageDir = LTR;
432    String subMessage = String();
433    if (message.isEmpty())
434        client->hideValidationMessage(*this);
435    else
436        findCustomValidationMessageTextDirection(message, messageDir, subMessage, subMessageDir);
437    client->showValidationMessage(*this, message, messageDir, subMessage, subMessageDir);
438}
439
440void HTMLFormControlElement::hideVisibleValidationMessage()
441{
442    if (!m_hasValidationMessage)
443        return;
444
445    if (ValidationMessageClient* client = validationMessageClient())
446        client->hideValidationMessage(*this);
447}
448
449bool HTMLFormControlElement::isValidationMessageVisible() const
450{
451    if (!m_hasValidationMessage)
452        return false;
453
454    ValidationMessageClient* client = validationMessageClient();
455    if (!client)
456        return false;
457
458    return client->isValidationMessageVisible(*this);
459}
460
461ValidationMessageClient* HTMLFormControlElement::validationMessageClient() const
462{
463    Page* page = document().page();
464    if (!page)
465        return 0;
466
467    return &page->validationMessageClient();
468}
469
470bool HTMLFormControlElement::checkValidity(WillBeHeapVector<RefPtrWillBeMember<FormAssociatedElement> >* unhandledInvalidControls)
471{
472    if (!willValidate() || isValidFormControlElement())
473        return true;
474    // An event handler can deref this object.
475    RefPtrWillBeRawPtr<HTMLFormControlElement> protector(this);
476    RefPtrWillBeRawPtr<Document> originalDocument(document());
477    bool needsDefaultAction = dispatchEvent(Event::createCancelable(EventTypeNames::invalid));
478    if (needsDefaultAction && unhandledInvalidControls && inDocument() && originalDocument == document())
479        unhandledInvalidControls->append(this);
480    return false;
481}
482
483bool HTMLFormControlElement::isValidFormControlElement()
484{
485    // If the following assertion fails, setNeedsValidityCheck() is not called
486    // correctly when something which changes validity is updated.
487    ASSERT(m_isValid == valid());
488    return m_isValid;
489}
490
491void HTMLFormControlElement::setNeedsValidityCheck()
492{
493    bool newIsValid = valid();
494    if (willValidate() && newIsValid != m_isValid) {
495        // Update style for pseudo classes such as :valid :invalid.
496        setNeedsStyleRecalc(SubtreeStyleChange);
497    }
498    m_isValid = newIsValid;
499
500    // Updates only if this control already has a validation message.
501    if (isValidationMessageVisible()) {
502        // Calls updateVisibleValidationMessage() even if m_isValid is not
503        // changed because a validation message can be changed.
504        updateVisibleValidationMessage();
505    }
506}
507
508void HTMLFormControlElement::setCustomValidity(const String& error)
509{
510    FormAssociatedElement::setCustomValidity(error);
511    setNeedsValidityCheck();
512}
513
514void HTMLFormControlElement::dispatchBlurEvent(Element* newFocusedElement)
515{
516    HTMLElement::dispatchBlurEvent(newFocusedElement);
517    hideVisibleValidationMessage();
518}
519
520bool HTMLFormControlElement::isSuccessfulSubmitButton() const
521{
522    return canBeSuccessfulSubmitButton() && !isDisabledFormControl();
523}
524
525bool HTMLFormControlElement::isDefaultButtonForForm() const
526{
527    return isSuccessfulSubmitButton() && form() && form()->defaultButton() == this;
528}
529
530HTMLFormControlElement* HTMLFormControlElement::enclosingFormControlElement(Node* node)
531{
532    if (!node)
533        return 0;
534    return Traversal<HTMLFormControlElement>::firstAncestorOrSelf(*node);
535}
536
537String HTMLFormControlElement::nameForAutofill() const
538{
539    String fullName = name();
540    String trimmedName = fullName.stripWhiteSpace();
541    if (!trimmedName.isEmpty())
542        return trimmedName;
543    fullName = getIdAttribute();
544    trimmedName = fullName.stripWhiteSpace();
545    return trimmedName;
546}
547
548void HTMLFormControlElement::setFocus(bool flag)
549{
550    LabelableElement::setFocus(flag);
551
552    if (!flag && wasChangedSinceLastFormControlChangeEvent())
553        dispatchFormControlChangeEvent();
554}
555
556} // namespace blink
557