Keep auxilliary media objects on the heap always.
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / HTMLFormControlElement.cpp
blob66aea16e5c23dcb11d1f472c81b9f400bb76604b
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)
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.
25 #include "config.h"
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"
45 namespace blink {
47 using namespace HTMLNames;
49 HTMLFormControlElement::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_validityIsDirty(false)
62 , m_wasChangedSinceLastFormControlChangeEvent(false)
63 , m_wasFocusedByMouse(false)
65 setHasCustomStyleCallbacks();
66 associateByParser(form);
69 HTMLFormControlElement::~HTMLFormControlElement()
71 #if !ENABLE(OILPAN)
72 #if ENABLE(ASSERT)
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();
77 #endif
78 setForm(0);
79 #endif
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())
92 return emptyString();
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);
128 break;
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);
143 resetImpl();
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));
162 if (layoutObject())
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);
174 } else {
175 HTMLElement::parseAttribute(name, value);
179 void HTMLFormControlElement::disabledAttributeChanged()
181 setNeedsWillValidateCheck();
182 pseudoStateChanged(CSSSelector::PseudoDisabled);
183 pseudoStateChanged(CSSSelector::PseudoEnabled);
184 if (layoutObject())
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
188 // here.
189 document().setNeedsFocusedElementCheck();
193 void HTMLFormControlElement::requiredAttributeChanged()
195 setNeedsValidityCheck();
196 pseudoStateChanged(CSSSelector::PseudoRequired);
197 pseudoStateChanged(CSSSelector::PseudoOptional);
200 bool HTMLFormControlElement::supportsAutofocus() const
202 return false;
205 bool HTMLFormControlElement::isAutofocusable() const
207 return fastHasAttribute(autofocusAttr) && supportsAutofocus();
210 void HTMLFormControlElement::setAutofilled(bool autofilled)
212 if (autofilled == m_isAutofilled)
213 return;
215 m_isAutofilled = autofilled;
216 pseudoStateChanged(CSSSelector::PseudoAutofill);
219 static bool shouldAutofocusOnAttach(const HTMLFormControlElement* element)
221 if (!element->isAutofocusable())
222 return false;
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."));
226 return false;
229 return true;
232 void HTMLFormControlElement::attach(const AttachContext& context)
234 HTMLElement::attach(context);
236 if (!layoutObject())
237 return;
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
245 // the standard.
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)
307 if (!node)
308 return;
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
344 if (m_disabled)
345 return true;
347 if (m_ancestorDisabledState == AncestorDisabledStateUnknown)
348 updateAncestorDisabledState();
349 return m_ancestorDisabledState == AncestorDisabledStateDisabled;
352 bool HTMLFormControlElement::isRequired() const
354 return m_isRequired;
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
381 return false;
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)
400 return;
401 if (!event.isKeyboardEvent() || event.type() != EventTypeNames::keydown)
402 return;
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;
424 else
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();
434 } else {
435 // If the following assertion fails, setNeedsWillValidateCheck() is not
436 // called correctly when something which changes recalcWillValidate() result
437 // is updated.
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)
448 return;
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.
456 if (!m_willValidate)
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();
471 if (!page)
472 return;
473 String message;
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);
484 else
485 findCustomValidationMessageTextDirection(message, messageDir, subMessage, subMessageDir);
486 client->showValidationMessage(*this, message, messageDir, subMessage, subMessageDir);
489 void HTMLFormControlElement::hideVisibleValidationMessage()
491 if (!m_hasValidationMessage)
492 return;
494 if (ValidationMessageClient* client = validationMessageClient())
495 client->hideValidationMessage(*this);
498 bool HTMLFormControlElement::isValidationMessageVisible() const
500 if (!m_hasValidationMessage)
501 return false;
503 ValidationMessageClient* client = validationMessageClient();
504 if (!client)
505 return false;
507 return client->isValidationMessageVisible(*this);
510 ValidationMessageClient* HTMLFormControlElement::validationMessageClient() const
512 Page* page = document().page();
513 if (!page)
514 return nullptr;
516 return &page->validationMessageClient();
519 bool HTMLFormControlElement::checkValidity(WillBeHeapVector<RefPtrWillBeMember<HTMLFormControlElement>>* unhandledInvalidControls, CheckValidityEventBehavior eventBehavior)
521 if (isValidElement())
522 return true;
523 if (eventBehavior != CheckValidityDispatchInvalidEvent)
524 return false;
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);
531 return false;
534 void HTMLFormControlElement::showValidationMessage()
536 scrollIntoViewIfNeeded(false);
537 RefPtrWillBeRawPtr<HTMLFormControlElement> protector(this);
538 focus();
539 updateVisibleValidationMessage();
542 bool HTMLFormControlElement::reportValidity()
544 WillBeHeapVector<RefPtrWillBeMember<HTMLFormControlElement>> unhandledInvalidControls;
545 bool isValid = checkValidity(&unhandledInvalidControls, CheckValidityDispatchInvalidEvent);
546 if (isValid || unhandledInvalidControls.isEmpty())
547 return isValid;
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();
553 if (isFocusable()) {
554 showValidationMessage();
555 return false;
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));
562 return false;
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;
575 } else {
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()));
580 return m_isValid;
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)
627 if (!node)
628 return nullptr;
629 return Traversal<HTMLFormControlElement>::firstAncestorOrSelf(*node);
632 String HTMLFormControlElement::nameForAutofill() const
634 String fullName = name();
635 String trimmedName = fullName.stripWhiteSpace();
636 if (!trimmedName.isEmpty())
637 return trimmedName;
638 fullName = getIdAttribute();
639 trimmedName = fullName.stripWhiteSpace();
640 return trimmedName;
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();
657 } // namespace blink