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/html/parser/HTMLParserIdioms.h"
38 #include "core/inspector/ConsoleMessage.h"
39 #include "core/layout/LayoutBox.h"
40 #include "core/layout/LayoutTheme.h"
41 #include "core/page/Page.h"
42 #include "core/page/ValidationMessageClient.h"
43 #include "platform/text/BidiTextRun.h"
44 #include "wtf/Vector.h"
48 using namespace HTMLNames
;
50 HTMLFormControlElement::HTMLFormControlElement(const QualifiedName
& tagName
, Document
& document
, HTMLFormElement
* form
)
51 : LabelableElement(tagName
, document
)
53 , m_isAutofilled(false)
56 , m_hasValidationMessage(false)
57 , m_ancestorDisabledState(AncestorDisabledStateUnknown
)
58 , m_dataListAncestorState(Unknown
)
59 , m_willValidateInitialized(false)
60 , m_willValidate(true)
62 , m_validityIsDirty(false)
63 , m_wasChangedSinceLastFormControlChangeEvent(false)
64 , m_wasFocusedByMouse(false)
66 setHasCustomStyleCallbacks();
67 associateByParser(form
);
70 HTMLFormControlElement::~HTMLFormControlElement()
74 // Recalculate m_willValidate and m_isValid for the vtbl change in order to
75 // avoid assertion failures in isValidElement() called in setForm(0).
76 setNeedsWillValidateCheck();
77 setNeedsValidityCheck();
83 DEFINE_TRACE(HTMLFormControlElement
)
85 FormAssociatedElement::trace(visitor
);
86 LabelableElement::trace(visitor
);
89 String
HTMLFormControlElement::formAction() const
91 const AtomicString
& action
= fastGetAttribute(formactionAttr
);
93 return document().url();
94 return document().completeURL(stripLeadingAndTrailingHTMLSpaces(action
));
97 void HTMLFormControlElement::setFormAction(const AtomicString
& value
)
99 setAttribute(formactionAttr
, value
);
102 String
HTMLFormControlElement::formEnctype() const
104 const AtomicString
& formEnctypeAttr
= fastGetAttribute(formenctypeAttr
);
105 if (formEnctypeAttr
.isNull())
106 return emptyString();
107 return FormSubmission::Attributes::parseEncodingType(formEnctypeAttr
);
110 void HTMLFormControlElement::setFormEnctype(const AtomicString
& value
)
112 setAttribute(formenctypeAttr
, value
);
115 String
HTMLFormControlElement::formMethod() const
117 const AtomicString
& formMethodAttr
= fastGetAttribute(formmethodAttr
);
118 if (formMethodAttr
.isNull())
119 return emptyString();
120 return FormSubmission::Attributes::methodString(FormSubmission::Attributes::parseMethodType(formMethodAttr
));
123 void HTMLFormControlElement::setFormMethod(const AtomicString
& value
)
125 setAttribute(formmethodAttr
, value
);
128 bool HTMLFormControlElement::formNoValidate() const
130 return fastHasAttribute(formnovalidateAttr
);
133 void HTMLFormControlElement::updateAncestorDisabledState() const
135 HTMLFieldSetElement
* fieldSetAncestor
= 0;
136 ContainerNode
* legendAncestor
= 0;
137 for (HTMLElement
* ancestor
= Traversal
<HTMLElement
>::firstAncestor(*this); ancestor
; ancestor
= Traversal
<HTMLElement
>::firstAncestor(*ancestor
)) {
138 if (!legendAncestor
&& isHTMLLegendElement(*ancestor
))
139 legendAncestor
= ancestor
;
140 if (isHTMLFieldSetElement(*ancestor
)) {
141 fieldSetAncestor
= toHTMLFieldSetElement(ancestor
);
145 m_ancestorDisabledState
= (fieldSetAncestor
&& fieldSetAncestor
->isDisabledFormControl() && !(legendAncestor
&& legendAncestor
== fieldSetAncestor
->legend())) ? AncestorDisabledStateDisabled
: AncestorDisabledStateEnabled
;
148 void HTMLFormControlElement::ancestorDisabledStateWasChanged()
150 m_ancestorDisabledState
= AncestorDisabledStateUnknown
;
151 disabledAttributeChanged();
154 void HTMLFormControlElement::reset()
156 setAutofilled(false);
160 void HTMLFormControlElement::parseAttribute(const QualifiedName
& name
, const AtomicString
& value
)
162 if (name
== formAttr
) {
163 formAttributeChanged();
164 UseCounter::count(document(), UseCounter::FormAttribute
);
165 } else if (name
== disabledAttr
) {
166 bool oldDisabled
= m_disabled
;
167 m_disabled
= !value
.isNull();
168 if (oldDisabled
!= m_disabled
)
169 disabledAttributeChanged();
170 } else if (name
== readonlyAttr
) {
171 bool wasReadOnly
= m_isReadOnly
;
172 m_isReadOnly
= !value
.isNull();
173 if (wasReadOnly
!= m_isReadOnly
) {
174 setNeedsWillValidateCheck();
175 setNeedsStyleRecalc(SubtreeStyleChange
, StyleChangeReasonForTracing::fromAttribute(name
));
177 LayoutTheme::theme().controlStateChanged(*layoutObject(), ReadOnlyControlState
);
179 } else if (name
== requiredAttr
) {
180 bool wasRequired
= m_isRequired
;
181 m_isRequired
= !value
.isNull();
182 if (wasRequired
!= m_isRequired
)
183 requiredAttributeChanged();
184 UseCounter::count(document(), UseCounter::RequiredAttribute
);
185 } else if (name
== autofocusAttr
) {
186 HTMLElement::parseAttribute(name
, value
);
187 UseCounter::count(document(), UseCounter::AutoFocusAttribute
);
189 HTMLElement::parseAttribute(name
, value
);
193 void HTMLFormControlElement::disabledAttributeChanged()
195 setNeedsWillValidateCheck();
196 pseudoStateChanged(CSSSelector::PseudoDisabled
);
197 pseudoStateChanged(CSSSelector::PseudoEnabled
);
199 LayoutTheme::theme().controlStateChanged(*layoutObject(), EnabledControlState
);
200 if (isDisabledFormControl() && treeScope().adjustedFocusedElement() == this) {
201 // We might want to call blur(), but it's dangerous to dispatch events
203 document().setNeedsFocusedElementCheck();
207 void HTMLFormControlElement::requiredAttributeChanged()
209 setNeedsValidityCheck();
210 pseudoStateChanged(CSSSelector::PseudoRequired
);
211 pseudoStateChanged(CSSSelector::PseudoOptional
);
214 bool HTMLFormControlElement::supportsAutofocus() const
219 bool HTMLFormControlElement::isAutofocusable() const
221 return fastHasAttribute(autofocusAttr
) && supportsAutofocus();
224 void HTMLFormControlElement::setAutofilled(bool autofilled
)
226 if (autofilled
== m_isAutofilled
)
229 m_isAutofilled
= autofilled
;
230 pseudoStateChanged(CSSSelector::PseudoAutofill
);
233 static bool shouldAutofocusOnAttach(const HTMLFormControlElement
* element
)
235 if (!element
->isAutofocusable())
237 if (element
->document().isSandboxed(SandboxAutomaticFeatures
)) {
238 // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
239 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."));
246 void HTMLFormControlElement::attach(const AttachContext
& context
)
248 HTMLElement::attach(context
);
253 // The call to updateFromElement() needs to go after the call through
254 // to the base class's attach() because that can sometimes do a close
255 // on the layoutObject.
256 layoutObject()->updateFromElement();
258 // FIXME: Autofocus handling should be moved to insertedInto according to
260 if (shouldAutofocusOnAttach(this))
261 document().setAutofocusElement(this);
264 void HTMLFormControlElement::didMoveToNewDocument(Document
& oldDocument
)
266 FormAssociatedElement::didMoveToNewDocument(oldDocument
);
267 HTMLElement::didMoveToNewDocument(oldDocument
);
270 Node::InsertionNotificationRequest
HTMLFormControlElement::insertedInto(ContainerNode
* insertionPoint
)
272 m_ancestorDisabledState
= AncestorDisabledStateUnknown
;
273 m_dataListAncestorState
= Unknown
;
274 setNeedsWillValidateCheck();
275 HTMLElement::insertedInto(insertionPoint
);
276 FormAssociatedElement::insertedInto(insertionPoint
);
277 fieldSetAncestorsSetNeedsValidityCheck(insertionPoint
);
279 // Trigger for elements outside of forms.
280 if (!formOwner() && insertionPoint
->inDocument())
281 document().didAssociateFormControl(this);
283 return InsertionDone
;
286 void HTMLFormControlElement::removedFrom(ContainerNode
* insertionPoint
)
288 fieldSetAncestorsSetNeedsValidityCheck(insertionPoint
);
289 hideVisibleValidationMessage();
290 m_hasValidationMessage
= false;
291 m_ancestorDisabledState
= AncestorDisabledStateUnknown
;
292 m_dataListAncestorState
= Unknown
;
293 setNeedsWillValidateCheck();
294 HTMLElement::removedFrom(insertionPoint
);
295 FormAssociatedElement::removedFrom(insertionPoint
);
296 document().removeFormAssociation(this);
299 void HTMLFormControlElement::willChangeForm()
301 FormAssociatedElement::willChangeForm();
302 formOwnerSetNeedsValidityCheck();
305 void HTMLFormControlElement::didChangeForm()
307 FormAssociatedElement::didChangeForm();
308 formOwnerSetNeedsValidityCheck();
311 void HTMLFormControlElement::formOwnerSetNeedsValidityCheck()
313 if (HTMLFormElement
* form
= formOwner()) {
314 form
->pseudoStateChanged(CSSSelector::PseudoValid
);
315 form
->pseudoStateChanged(CSSSelector::PseudoInvalid
);
319 void HTMLFormControlElement::fieldSetAncestorsSetNeedsValidityCheck(Node
* node
)
323 for (HTMLFieldSetElement
* fieldSet
= Traversal
<HTMLFieldSetElement
>::firstAncestorOrSelf(*node
); fieldSet
; fieldSet
= Traversal
<HTMLFieldSetElement
>::firstAncestor(*fieldSet
)) {
324 fieldSet
->pseudoStateChanged(CSSSelector::PseudoValid
);
325 fieldSet
->pseudoStateChanged(CSSSelector::PseudoInvalid
);
329 void HTMLFormControlElement::setChangedSinceLastFormControlChangeEvent(bool changed
)
331 m_wasChangedSinceLastFormControlChangeEvent
= changed
;
334 void HTMLFormControlElement::dispatchChangeEvent()
336 dispatchScopedEvent(Event::createBubble(EventTypeNames::change
));
339 void HTMLFormControlElement::dispatchFormControlChangeEvent()
341 dispatchChangeEvent();
342 setChangedSinceLastFormControlChangeEvent(false);
345 void HTMLFormControlElement::dispatchFormControlInputEvent()
347 setChangedSinceLastFormControlChangeEvent(true);
348 HTMLElement::dispatchInputEvent();
351 HTMLFormElement
* HTMLFormControlElement::formOwner() const
353 return FormAssociatedElement::form();
356 bool HTMLFormControlElement::isDisabledFormControl() const
361 if (m_ancestorDisabledState
== AncestorDisabledStateUnknown
)
362 updateAncestorDisabledState();
363 return m_ancestorDisabledState
== AncestorDisabledStateDisabled
;
366 bool HTMLFormControlElement::isRequired() const
371 String
HTMLFormControlElement::resultForDialogSubmit()
373 return fastGetAttribute(valueAttr
);
376 void HTMLFormControlElement::didRecalcStyle(StyleRecalcChange
)
378 if (LayoutObject
* layoutObject
= this->layoutObject())
379 layoutObject
->updateFromElement();
382 bool HTMLFormControlElement::supportsFocus() const
384 return !isDisabledFormControl();
387 bool HTMLFormControlElement::isKeyboardFocusable() const
389 // Skip tabIndex check in a parent class.
390 return isFocusable();
393 bool HTMLFormControlElement::shouldShowFocusRingOnMouseFocus() const
398 bool HTMLFormControlElement::shouldHaveFocusAppearance() const
400 return !m_wasFocusedByMouse
|| shouldShowFocusRingOnMouseFocus();
403 void HTMLFormControlElement::dispatchFocusEvent(Element
* oldFocusedElement
, WebFocusType type
, InputDeviceCapabilities
* sourceCapabilities
)
405 if (type
!= WebFocusTypePage
)
406 m_wasFocusedByMouse
= type
== WebFocusTypeMouse
;
407 // ContainerNode::handleStyleChangeOnFocusStateChange() will inform LayoutTheme about the focus state change.
408 HTMLElement::dispatchFocusEvent(oldFocusedElement
, type
, sourceCapabilities
);
411 void HTMLFormControlElement::willCallDefaultEventHandler(const Event
& event
)
413 if (!m_wasFocusedByMouse
)
415 if (!event
.isKeyboardEvent() || event
.type() != EventTypeNames::keydown
)
418 bool oldShouldHaveFocusAppearance
= shouldHaveFocusAppearance();
419 m_wasFocusedByMouse
= false;
421 // Change of m_wasFocusByMouse may affect shouldHaveFocusAppearance() and LayoutTheme::isFocused().
422 // Inform LayoutTheme if shouldHaveFocusAppearance() changes.
423 if (oldShouldHaveFocusAppearance
!= shouldHaveFocusAppearance() && layoutObject())
424 LayoutTheme::theme().controlStateChanged(*layoutObject(), FocusControlState
);
427 short HTMLFormControlElement::tabIndex() const
429 // Skip the supportsFocus check in HTMLElement.
430 return Element::tabIndex();
433 bool HTMLFormControlElement::recalcWillValidate() const
435 if (m_dataListAncestorState
== Unknown
) {
436 if (Traversal
<HTMLDataListElement
>::firstAncestor(*this))
437 m_dataListAncestorState
= InsideDataList
;
439 m_dataListAncestorState
= NotInsideDataList
;
441 return m_dataListAncestorState
== NotInsideDataList
&& !isDisabledOrReadOnly();
444 bool HTMLFormControlElement::willValidate() const
446 if (!m_willValidateInitialized
|| m_dataListAncestorState
== Unknown
) {
447 const_cast<HTMLFormControlElement
*>(this)->setNeedsWillValidateCheck();
449 // If the following assertion fails, setNeedsWillValidateCheck() is not
450 // called correctly when something which changes recalcWillValidate() result
452 ASSERT(m_willValidate
== recalcWillValidate());
454 return m_willValidate
;
457 void HTMLFormControlElement::setNeedsWillValidateCheck()
459 // We need to recalculate willValidate immediately because willValidate change can causes style change.
460 bool newWillValidate
= recalcWillValidate();
461 if (m_willValidateInitialized
&& m_willValidate
== newWillValidate
)
463 m_willValidateInitialized
= true;
464 m_willValidate
= newWillValidate
;
465 setNeedsValidityCheck();
466 // No need to trigger style recalculation here because
467 // setNeedsValidityCheck() does it in the right away. This relies on
468 // the assumption that valid() is always true if willValidate() is false.
471 hideVisibleValidationMessage();
474 void HTMLFormControlElement::findCustomValidationMessageTextDirection(const String
& message
, TextDirection
&messageDir
, String
& subMessage
, TextDirection
&subMessageDir
)
476 subMessage
= fastGetAttribute(titleAttr
);
477 messageDir
= determineDirectionality(message
);
478 if (!subMessage
.isEmpty())
479 subMessageDir
= layoutObject()->style()->direction();
482 void HTMLFormControlElement::updateVisibleValidationMessage()
484 Page
* page
= document().page();
488 if (layoutObject() && willValidate())
489 message
= validationMessage().stripWhiteSpace();
491 m_hasValidationMessage
= true;
492 ValidationMessageClient
* client
= &page
->validationMessageClient();
493 TextDirection messageDir
= LTR
;
494 TextDirection subMessageDir
= LTR
;
495 String subMessage
= String();
496 if (message
.isEmpty())
497 client
->hideValidationMessage(*this);
499 findCustomValidationMessageTextDirection(message
, messageDir
, subMessage
, subMessageDir
);
500 client
->showValidationMessage(*this, message
, messageDir
, subMessage
, subMessageDir
);
503 void HTMLFormControlElement::hideVisibleValidationMessage()
505 if (!m_hasValidationMessage
)
508 if (ValidationMessageClient
* client
= validationMessageClient())
509 client
->hideValidationMessage(*this);
512 bool HTMLFormControlElement::isValidationMessageVisible() const
514 if (!m_hasValidationMessage
)
517 ValidationMessageClient
* client
= validationMessageClient();
521 return client
->isValidationMessageVisible(*this);
524 ValidationMessageClient
* HTMLFormControlElement::validationMessageClient() const
526 Page
* page
= document().page();
530 return &page
->validationMessageClient();
533 bool HTMLFormControlElement::checkValidity(WillBeHeapVector
<RefPtrWillBeMember
<HTMLFormControlElement
>>* unhandledInvalidControls
, CheckValidityEventBehavior eventBehavior
)
535 if (isValidElement())
537 if (eventBehavior
!= CheckValidityDispatchInvalidEvent
)
539 // An event handler can deref this object.
540 RefPtrWillBeRawPtr
<HTMLFormControlElement
> protector(this);
541 RefPtrWillBeRawPtr
<Document
> originalDocument(document());
542 bool needsDefaultAction
= dispatchEvent(Event::createCancelable(EventTypeNames::invalid
));
543 if (needsDefaultAction
&& unhandledInvalidControls
&& inDocument() && originalDocument
== document())
544 unhandledInvalidControls
->append(this);
548 void HTMLFormControlElement::showValidationMessage()
550 scrollIntoViewIfNeeded(false);
551 RefPtrWillBeRawPtr
<HTMLFormControlElement
> protector(this);
553 updateVisibleValidationMessage();
556 bool HTMLFormControlElement::reportValidity()
558 WillBeHeapVector
<RefPtrWillBeMember
<HTMLFormControlElement
>> unhandledInvalidControls
;
559 bool isValid
= checkValidity(&unhandledInvalidControls
, CheckValidityDispatchInvalidEvent
);
560 if (isValid
|| unhandledInvalidControls
.isEmpty())
562 ASSERT(unhandledInvalidControls
.size() == 1);
563 ASSERT(unhandledInvalidControls
[0].get() == this);
564 // Update layout now before calling isFocusable(), which has
565 // !layoutObject()->needsLayout() assertion.
566 document().updateLayoutIgnorePendingStylesheets();
568 showValidationMessage();
571 if (document().frame()) {
572 String
message("An invalid form control with name='%name' is not focusable.");
573 message
.replace("%name", name());
574 document().addConsoleMessage(ConsoleMessage::create(RenderingMessageSource
, ErrorMessageLevel
, message
));
579 bool HTMLFormControlElement::matchesValidityPseudoClasses() const
581 return willValidate();
584 bool HTMLFormControlElement::isValidElement()
586 if (m_validityIsDirty
) {
587 m_isValid
= !willValidate() || valid();
588 m_validityIsDirty
= false;
590 // If the following assertion fails, setNeedsValidityCheck() is not
591 // called correctly when something which changes validity is updated.
592 ASSERT(m_isValid
== (!willValidate() || valid()));
597 void HTMLFormControlElement::setNeedsValidityCheck()
599 if (!m_validityIsDirty
) {
600 m_validityIsDirty
= true;
601 formOwnerSetNeedsValidityCheck();
602 fieldSetAncestorsSetNeedsValidityCheck(parentNode());
603 pseudoStateChanged(CSSSelector::PseudoValid
);
604 pseudoStateChanged(CSSSelector::PseudoInvalid
);
607 // Updates only if this control already has a validation message.
608 if (isValidationMessageVisible()) {
609 // Calls updateVisibleValidationMessage() even if m_isValid is not
610 // changed because a validation message can be changed.
611 updateVisibleValidationMessage();
615 void HTMLFormControlElement::setCustomValidity(const String
& error
)
617 FormAssociatedElement::setCustomValidity(error
);
618 setNeedsValidityCheck();
621 void HTMLFormControlElement::dispatchBlurEvent(Element
* newFocusedElement
, WebFocusType type
, InputDeviceCapabilities
* sourceCapabilities
)
623 if (type
!= WebFocusTypePage
)
624 m_wasFocusedByMouse
= false;
625 HTMLElement::dispatchBlurEvent(newFocusedElement
, type
, sourceCapabilities
);
626 hideVisibleValidationMessage();
629 bool HTMLFormControlElement::isSuccessfulSubmitButton() const
631 return canBeSuccessfulSubmitButton() && !isDisabledFormControl();
634 bool HTMLFormControlElement::isDefaultButtonForForm() const
636 return isSuccessfulSubmitButton() && form() && form()->defaultButton() == this;
639 HTMLFormControlElement
* HTMLFormControlElement::enclosingFormControlElement(Node
* node
)
643 return Traversal
<HTMLFormControlElement
>::firstAncestorOrSelf(*node
);
646 String
HTMLFormControlElement::nameForAutofill() const
648 String fullName
= name();
649 String trimmedName
= fullName
.stripWhiteSpace();
650 if (!trimmedName
.isEmpty())
652 fullName
= getIdAttribute();
653 trimmedName
= fullName
.stripWhiteSpace();
657 void HTMLFormControlElement::setFocus(bool flag
)
659 LabelableElement::setFocus(flag
);
661 if (!flag
&& wasChangedSinceLastFormControlChangeEvent())
662 dispatchFormControlChangeEvent();
665 void HTMLFormControlElement::copyNonAttributePropertiesFromElement(const Element
& source
)
667 HTMLElement::copyNonAttributePropertiesFromElement(source
);
668 setNeedsValidityCheck();