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)
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.
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.
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.
26 #include "core/html/HTMLFormControlElement.h"
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/layout/LayoutBox.h"
39 #include "core/layout/LayoutTheme.h"
40 #include "core/page/Page.h"
41 #include "core/page/ValidationMessageClient.h"
42 #include "platform/text/BidiTextRun.h"
43 #include "wtf/Vector.h"
47 using namespace HTMLNames
;
49 HTMLFormControlElement::HTMLFormControlElement(const QualifiedName
& tagName
, Document
& document
, HTMLFormElement
* form
)
50 : LabelableElement(tagName
, document
)
52 , m_isAutofilled(false)
55 , m_hasValidationMessage(false)
56 , m_ancestorDisabledState(AncestorDisabledStateUnknown
)
57 , m_dataListAncestorState(Unknown
)
58 , m_willValidateInitialized(false)
59 , m_willValidate(true)
61 , m_validityIsDirty(false)
62 , m_wasChangedSinceLastFormControlChangeEvent(false)
63 , m_wasFocusedByMouse(false)
65 setHasCustomStyleCallbacks();
66 associateByParser(form
);
69 HTMLFormControlElement::~HTMLFormControlElement()
73 // Recalculate m_willValidate and m_isValid for the vtbl change in order to
74 // avoid assertion failures in isValidElement() called in setForm(0).
75 setNeedsWillValidateCheck();
76 setNeedsValidityCheck();
82 DEFINE_TRACE(HTMLFormControlElement
)
84 FormAssociatedElement::trace(visitor
);
85 LabelableElement::trace(visitor
);
88 String
HTMLFormControlElement::formEnctype() const
90 const AtomicString
& formEnctypeAttr
= fastGetAttribute(formenctypeAttr
);
91 if (formEnctypeAttr
.isNull())
93 return FormSubmission::Attributes::parseEncodingType(formEnctypeAttr
);
96 void HTMLFormControlElement::setFormEnctype(const AtomicString
& value
)
98 setAttribute(formenctypeAttr
, value
);
101 String
HTMLFormControlElement::formMethod() const
103 const AtomicString
& formMethodAttr
= fastGetAttribute(formmethodAttr
);
104 if (formMethodAttr
.isNull())
105 return emptyString();
106 return FormSubmission::Attributes::methodString(FormSubmission::Attributes::parseMethodType(formMethodAttr
));
109 void HTMLFormControlElement::setFormMethod(const AtomicString
& value
)
111 setAttribute(formmethodAttr
, value
);
114 bool HTMLFormControlElement::formNoValidate() const
116 return fastHasAttribute(formnovalidateAttr
);
119 void HTMLFormControlElement::updateAncestorDisabledState() const
121 HTMLFieldSetElement
* fieldSetAncestor
= 0;
122 ContainerNode
* legendAncestor
= 0;
123 for (HTMLElement
* ancestor
= Traversal
<HTMLElement
>::firstAncestor(*this); ancestor
; ancestor
= Traversal
<HTMLElement
>::firstAncestor(*ancestor
)) {
124 if (!legendAncestor
&& isHTMLLegendElement(*ancestor
))
125 legendAncestor
= ancestor
;
126 if (isHTMLFieldSetElement(*ancestor
)) {
127 fieldSetAncestor
= toHTMLFieldSetElement(ancestor
);
131 m_ancestorDisabledState
= (fieldSetAncestor
&& fieldSetAncestor
->isDisabledFormControl() && !(legendAncestor
&& legendAncestor
== fieldSetAncestor
->legend())) ? AncestorDisabledStateDisabled
: AncestorDisabledStateEnabled
;
134 void HTMLFormControlElement::ancestorDisabledStateWasChanged()
136 m_ancestorDisabledState
= AncestorDisabledStateUnknown
;
137 disabledAttributeChanged();
140 void HTMLFormControlElement::reset()
142 setAutofilled(false);
146 void HTMLFormControlElement::parseAttribute(const QualifiedName
& name
, const AtomicString
& value
)
148 if (name
== formAttr
) {
149 formAttributeChanged();
150 UseCounter::count(document(), UseCounter::FormAttribute
);
151 } else if (name
== disabledAttr
) {
152 bool oldDisabled
= m_disabled
;
153 m_disabled
= !value
.isNull();
154 if (oldDisabled
!= m_disabled
)
155 disabledAttributeChanged();
156 } else if (name
== readonlyAttr
) {
157 bool wasReadOnly
= m_isReadOnly
;
158 m_isReadOnly
= !value
.isNull();
159 if (wasReadOnly
!= m_isReadOnly
) {
160 setNeedsWillValidateCheck();
161 setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::fromAttribute(name
));
163 LayoutTheme::theme().controlStateChanged(*layoutObject(), ReadOnlyControlState
);
165 } else if (name
== requiredAttr
) {
166 bool wasRequired
= m_isRequired
;
167 m_isRequired
= !value
.isNull();
168 if (wasRequired
!= m_isRequired
)
169 requiredAttributeChanged();
170 UseCounter::count(document(), UseCounter::RequiredAttribute
);
171 } else if (name
== autofocusAttr
) {
172 HTMLElement::parseAttribute(name
, value
);
173 UseCounter::count(document(), UseCounter::AutoFocusAttribute
);
175 HTMLElement::parseAttribute(name
, value
);
179 void HTMLFormControlElement::disabledAttributeChanged()
181 setNeedsWillValidateCheck();
182 pseudoStateChanged(CSSSelector::PseudoDisabled
);
183 pseudoStateChanged(CSSSelector::PseudoEnabled
);
185 LayoutTheme::theme().controlStateChanged(*layoutObject(), EnabledControlState
);
186 if (isDisabledFormControl() && treeScope().adjustedFocusedElement() == this) {
187 // We might want to call blur(), but it's dangerous to dispatch events
189 document().setNeedsFocusedElementCheck();
193 void HTMLFormControlElement::requiredAttributeChanged()
195 setNeedsValidityCheck();
196 pseudoStateChanged(CSSSelector::PseudoRequired
);
197 pseudoStateChanged(CSSSelector::PseudoOptional
);
200 bool HTMLFormControlElement::supportsAutofocus() const
205 bool HTMLFormControlElement::isAutofocusable() const
207 return fastHasAttribute(autofocusAttr
) && supportsAutofocus();
210 void HTMLFormControlElement::setAutofilled(bool autofilled
)
212 if (autofilled
== m_isAutofilled
)
215 m_isAutofilled
= autofilled
;
216 pseudoStateChanged(CSSSelector::PseudoAutofill
);
219 static bool shouldAutofocusOnAttach(const HTMLFormControlElement
* element
)
221 if (!element
->isAutofocusable())
223 if (element
->document().isSandboxed(SandboxAutomaticFeatures
)) {
224 // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
225 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."));
232 void HTMLFormControlElement::attach(const AttachContext
& context
)
234 HTMLElement::attach(context
);
239 // The call to updateFromElement() needs to go after the call through
240 // to the base class's attach() because that can sometimes do a close
241 // on the layoutObject.
242 layoutObject()->updateFromElement();
244 // FIXME: Autofocus handling should be moved to insertedInto according to
246 if (shouldAutofocusOnAttach(this))
247 document().setAutofocusElement(this);
250 void HTMLFormControlElement::didMoveToNewDocument(Document
& oldDocument
)
252 FormAssociatedElement::didMoveToNewDocument(oldDocument
);
253 HTMLElement::didMoveToNewDocument(oldDocument
);
256 Node::InsertionNotificationRequest
HTMLFormControlElement::insertedInto(ContainerNode
* insertionPoint
)
258 m_ancestorDisabledState
= AncestorDisabledStateUnknown
;
259 m_dataListAncestorState
= Unknown
;
260 setNeedsWillValidateCheck();
261 HTMLElement::insertedInto(insertionPoint
);
262 FormAssociatedElement::insertedInto(insertionPoint
);
263 fieldSetAncestorsSetNeedsValidityCheck(insertionPoint
);
265 // Trigger for elements outside of forms.
266 if (!formOwner() && insertionPoint
->inDocument())
267 document().didAssociateFormControl(this);
269 return InsertionDone
;
272 void HTMLFormControlElement::removedFrom(ContainerNode
* insertionPoint
)
274 fieldSetAncestorsSetNeedsValidityCheck(insertionPoint
);
275 hideVisibleValidationMessage();
276 m_hasValidationMessage
= false;
277 m_ancestorDisabledState
= AncestorDisabledStateUnknown
;
278 m_dataListAncestorState
= Unknown
;
279 setNeedsWillValidateCheck();
280 HTMLElement::removedFrom(insertionPoint
);
281 FormAssociatedElement::removedFrom(insertionPoint
);
282 document().removeFormAssociation(this);
285 void HTMLFormControlElement::willChangeForm()
287 FormAssociatedElement::willChangeForm();
288 formOwnerSetNeedsValidityCheck();
291 void HTMLFormControlElement::didChangeForm()
293 FormAssociatedElement::didChangeForm();
294 formOwnerSetNeedsValidityCheck();
297 void HTMLFormControlElement::formOwnerSetNeedsValidityCheck()
299 if (HTMLFormElement
* form
= formOwner()) {
300 form
->pseudoStateChanged(CSSSelector::PseudoValid
);
301 form
->pseudoStateChanged(CSSSelector::PseudoInvalid
);
305 void HTMLFormControlElement::fieldSetAncestorsSetNeedsValidityCheck(Node
* node
)
309 for (HTMLFieldSetElement
* fieldSet
= Traversal
<HTMLFieldSetElement
>::firstAncestorOrSelf(*node
); fieldSet
; fieldSet
= Traversal
<HTMLFieldSetElement
>::firstAncestor(*fieldSet
)) {
310 fieldSet
->pseudoStateChanged(CSSSelector::PseudoValid
);
311 fieldSet
->pseudoStateChanged(CSSSelector::PseudoInvalid
);
315 void HTMLFormControlElement::setChangedSinceLastFormControlChangeEvent(bool changed
)
317 m_wasChangedSinceLastFormControlChangeEvent
= changed
;
320 void HTMLFormControlElement::dispatchChangeEvent()
322 dispatchScopedEvent(Event::createBubble(EventTypeNames::change
));
325 void HTMLFormControlElement::dispatchFormControlChangeEvent()
327 dispatchChangeEvent();
328 setChangedSinceLastFormControlChangeEvent(false);
331 void HTMLFormControlElement::dispatchFormControlInputEvent()
333 setChangedSinceLastFormControlChangeEvent(true);
334 HTMLElement::dispatchInputEvent();
337 HTMLFormElement
* HTMLFormControlElement::formOwner() const
339 return FormAssociatedElement::form();
342 bool HTMLFormControlElement::isDisabledFormControl() const
347 if (m_ancestorDisabledState
== AncestorDisabledStateUnknown
)
348 updateAncestorDisabledState();
349 return m_ancestorDisabledState
== AncestorDisabledStateDisabled
;
352 bool HTMLFormControlElement::isRequired() const
357 String
HTMLFormControlElement::resultForDialogSubmit()
359 return fastGetAttribute(valueAttr
);
362 void HTMLFormControlElement::didRecalcStyle(StyleRecalcChange
)
364 if (LayoutObject
* layoutObject
= this->layoutObject())
365 layoutObject
->updateFromElement();
368 bool HTMLFormControlElement::supportsFocus() const
370 return !isDisabledFormControl();
373 bool HTMLFormControlElement::isKeyboardFocusable() const
375 // Skip tabIndex check in a parent class.
376 return isFocusable();
379 bool HTMLFormControlElement::shouldShowFocusRingOnMouseFocus() const
384 bool HTMLFormControlElement::shouldHaveFocusAppearance() const
386 return !m_wasFocusedByMouse
|| shouldShowFocusRingOnMouseFocus();
389 void HTMLFormControlElement::dispatchFocusEvent(Element
* oldFocusedElement
, WebFocusType type
, InputDeviceCapabilities
* sourceCapabilities
)
391 if (type
!= WebFocusTypePage
)
392 m_wasFocusedByMouse
= type
== WebFocusTypeMouse
;
393 // ContainerNode::handleStyleChangeOnFocusStateChange() will inform LayoutTheme about the focus state change.
394 HTMLElement::dispatchFocusEvent(oldFocusedElement
, type
, sourceCapabilities
);
397 void HTMLFormControlElement::willCallDefaultEventHandler(const Event
& event
)
399 if (!m_wasFocusedByMouse
)
401 if (!event
.isKeyboardEvent() || event
.type() != EventTypeNames::keydown
)
404 bool oldShouldHaveFocusAppearance
= shouldHaveFocusAppearance();
405 m_wasFocusedByMouse
= false;
407 // Change of m_wasFocusByMouse may affect shouldHaveFocusAppearance() and LayoutTheme::isFocused().
408 // Inform LayoutTheme if shouldHaveFocusAppearance() changes.
409 if (oldShouldHaveFocusAppearance
!= shouldHaveFocusAppearance() && layoutObject())
410 LayoutTheme::theme().controlStateChanged(*layoutObject(), FocusControlState
);
413 short HTMLFormControlElement::tabIndex() const
415 // Skip the supportsFocus check in HTMLElement.
416 return Element::tabIndex();
419 bool HTMLFormControlElement::recalcWillValidate() const
421 if (m_dataListAncestorState
== Unknown
) {
422 if (Traversal
<HTMLDataListElement
>::firstAncestor(*this))
423 m_dataListAncestorState
= InsideDataList
;
425 m_dataListAncestorState
= NotInsideDataList
;
427 return m_dataListAncestorState
== NotInsideDataList
&& !isDisabledOrReadOnly();
430 bool HTMLFormControlElement::willValidate() const
432 if (!m_willValidateInitialized
|| m_dataListAncestorState
== Unknown
) {
433 const_cast<HTMLFormControlElement
*>(this)->setNeedsWillValidateCheck();
435 // If the following assertion fails, setNeedsWillValidateCheck() is not
436 // called correctly when something which changes recalcWillValidate() result
438 ASSERT(m_willValidate
== recalcWillValidate());
440 return m_willValidate
;
443 void HTMLFormControlElement::setNeedsWillValidateCheck()
445 // We need to recalculate willValidate immediately because willValidate change can causes style change.
446 bool newWillValidate
= recalcWillValidate();
447 if (m_willValidateInitialized
&& m_willValidate
== newWillValidate
)
449 m_willValidateInitialized
= true;
450 m_willValidate
= newWillValidate
;
451 setNeedsValidityCheck();
452 // No need to trigger style recalculation here because
453 // setNeedsValidityCheck() does it in the right away. This relies on
454 // the assumption that valid() is always true if willValidate() is false.
457 hideVisibleValidationMessage();
460 void HTMLFormControlElement::findCustomValidationMessageTextDirection(const String
& message
, TextDirection
&messageDir
, String
& subMessage
, TextDirection
&subMessageDir
)
462 subMessage
= fastGetAttribute(titleAttr
);
463 messageDir
= determineDirectionality(message
);
464 if (!subMessage
.isEmpty())
465 subMessageDir
= layoutObject()->style()->direction();
468 void HTMLFormControlElement::updateVisibleValidationMessage()
470 Page
* page
= document().page();
474 if (layoutObject() && willValidate())
475 message
= validationMessage().stripWhiteSpace();
477 m_hasValidationMessage
= true;
478 ValidationMessageClient
* client
= &page
->validationMessageClient();
479 TextDirection messageDir
= LTR
;
480 TextDirection subMessageDir
= LTR
;
481 String subMessage
= String();
482 if (message
.isEmpty())
483 client
->hideValidationMessage(*this);
485 findCustomValidationMessageTextDirection(message
, messageDir
, subMessage
, subMessageDir
);
486 client
->showValidationMessage(*this, message
, messageDir
, subMessage
, subMessageDir
);
489 void HTMLFormControlElement::hideVisibleValidationMessage()
491 if (!m_hasValidationMessage
)
494 if (ValidationMessageClient
* client
= validationMessageClient())
495 client
->hideValidationMessage(*this);
498 bool HTMLFormControlElement::isValidationMessageVisible() const
500 if (!m_hasValidationMessage
)
503 ValidationMessageClient
* client
= validationMessageClient();
507 return client
->isValidationMessageVisible(*this);
510 ValidationMessageClient
* HTMLFormControlElement::validationMessageClient() const
512 Page
* page
= document().page();
516 return &page
->validationMessageClient();
519 bool HTMLFormControlElement::checkValidity(WillBeHeapVector
<RefPtrWillBeMember
<HTMLFormControlElement
>>* unhandledInvalidControls
, CheckValidityEventBehavior eventBehavior
)
521 if (isValidElement())
523 if (eventBehavior
!= CheckValidityDispatchInvalidEvent
)
525 // An event handler can deref this object.
526 RefPtrWillBeRawPtr
<HTMLFormControlElement
> protector(this);
527 RefPtrWillBeRawPtr
<Document
> originalDocument(document());
528 bool needsDefaultAction
= dispatchEvent(Event::createCancelable(EventTypeNames::invalid
));
529 if (needsDefaultAction
&& unhandledInvalidControls
&& inDocument() && originalDocument
== document())
530 unhandledInvalidControls
->append(this);
534 void HTMLFormControlElement::showValidationMessage()
536 scrollIntoViewIfNeeded(false);
537 RefPtrWillBeRawPtr
<HTMLFormControlElement
> protector(this);
539 updateVisibleValidationMessage();
542 bool HTMLFormControlElement::reportValidity()
544 WillBeHeapVector
<RefPtrWillBeMember
<HTMLFormControlElement
>> unhandledInvalidControls
;
545 bool isValid
= checkValidity(&unhandledInvalidControls
, CheckValidityDispatchInvalidEvent
);
546 if (isValid
|| unhandledInvalidControls
.isEmpty())
548 ASSERT(unhandledInvalidControls
.size() == 1);
549 ASSERT(unhandledInvalidControls
[0].get() == this);
550 // Update layout now before calling isFocusable(), which has
551 // !layoutObject()->needsLayout() assertion.
552 document().updateLayoutIgnorePendingStylesheets();
554 showValidationMessage();
557 if (document().frame()) {
558 String
message("An invalid form control with name='%name' is not focusable.");
559 message
.replace("%name", name());
560 document().addConsoleMessage(ConsoleMessage::create(RenderingMessageSource
, ErrorMessageLevel
, message
));
565 bool HTMLFormControlElement::matchesValidityPseudoClasses() const
567 return willValidate();
570 bool HTMLFormControlElement::isValidElement()
572 if (m_validityIsDirty
) {
573 m_isValid
= !willValidate() || valid();
574 m_validityIsDirty
= false;
576 // If the following assertion fails, setNeedsValidityCheck() is not
577 // called correctly when something which changes validity is updated.
578 ASSERT(m_isValid
== (!willValidate() || valid()));
583 void HTMLFormControlElement::setNeedsValidityCheck()
585 if (!m_validityIsDirty
) {
586 m_validityIsDirty
= true;
587 formOwnerSetNeedsValidityCheck();
588 fieldSetAncestorsSetNeedsValidityCheck(parentNode());
589 pseudoStateChanged(CSSSelector::PseudoValid
);
590 pseudoStateChanged(CSSSelector::PseudoInvalid
);
593 // Updates only if this control already has a validation message.
594 if (isValidationMessageVisible()) {
595 // Calls updateVisibleValidationMessage() even if m_isValid is not
596 // changed because a validation message can be changed.
597 updateVisibleValidationMessage();
601 void HTMLFormControlElement::setCustomValidity(const String
& error
)
603 FormAssociatedElement::setCustomValidity(error
);
604 setNeedsValidityCheck();
607 void HTMLFormControlElement::dispatchBlurEvent(Element
* newFocusedElement
, WebFocusType type
, InputDeviceCapabilities
* sourceCapabilities
)
609 if (type
!= WebFocusTypePage
)
610 m_wasFocusedByMouse
= false;
611 HTMLElement::dispatchBlurEvent(newFocusedElement
, type
, sourceCapabilities
);
612 hideVisibleValidationMessage();
615 bool HTMLFormControlElement::isSuccessfulSubmitButton() const
617 return canBeSuccessfulSubmitButton() && !isDisabledFormControl();
620 bool HTMLFormControlElement::isDefaultButtonForForm() const
622 return isSuccessfulSubmitButton() && form() && form()->defaultButton() == this;
625 HTMLFormControlElement
* HTMLFormControlElement::enclosingFormControlElement(Node
* node
)
629 return Traversal
<HTMLFormControlElement
>::firstAncestorOrSelf(*node
);
632 String
HTMLFormControlElement::nameForAutofill() const
634 String fullName
= name();
635 String trimmedName
= fullName
.stripWhiteSpace();
636 if (!trimmedName
.isEmpty())
638 fullName
= getIdAttribute();
639 trimmedName
= fullName
.stripWhiteSpace();
643 void HTMLFormControlElement::setFocus(bool flag
)
645 LabelableElement::setFocus(flag
);
647 if (!flag
&& wasChangedSinceLastFormControlChangeEvent())
648 dispatchFormControlChangeEvent();
651 void HTMLFormControlElement::copyNonAttributePropertiesFromElement(const Element
& source
)
653 HTMLElement::copyNonAttributePropertiesFromElement(source
);
654 setNeedsValidityCheck();