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-2018 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 "Autofill.h"
29#include "ControlStates.h"
30#include "ElementAncestorIterator.h"
31#include "Event.h"
32#include "EventHandler.h"
33#include "EventNames.h"
34#include "Frame.h"
35#include "FrameView.h"
36#include "HTMLFieldSetElement.h"
37#include "HTMLFormElement.h"
38#include "HTMLInputElement.h"
39#include "HTMLLegendElement.h"
40#include "HTMLTextAreaElement.h"
41#include "Quirks.h"
42#include "RenderBox.h"
43#include "RenderTheme.h"
44#include "ScriptDisallowedScope.h"
45#include "Settings.h"
46#include "StyleTreeResolver.h"
47#include "ValidationMessage.h"
48#include <wtf/IsoMallocInlines.h>
49#include <wtf/Ref.h>
50#include <wtf/Vector.h>
51
52namespace WebCore {
53
54WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLFormControlElement);
55
56using namespace HTMLNames;
57
58HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form)
59 : LabelableElement(tagName, document)
60 , FormAssociatedElement(form)
61 , m_disabled(false)
62 , m_isReadOnly(false)
63 , m_isRequired(false)
64 , m_valueMatchesRenderer(false)
65 , m_disabledByAncestorFieldset(false)
66 , m_dataListAncestorState(Unknown)
67 , m_willValidateInitialized(false)
68 , m_willValidate(true)
69 , m_isValid(true)
70 , m_wasChangedSinceLastFormControlChangeEvent(false)
71 , m_hasAutofocused(false)
72{
73 setHasCustomStyleResolveCallbacks();
74}
75
76HTMLFormControlElement::~HTMLFormControlElement()
77{
78 // The calls willChangeForm() and didChangeForm() are virtual, we want the
79 // form to be reset while this object still exists.
80 setForm(nullptr);
81}
82
83String HTMLFormControlElement::formEnctype() const
84{
85 const AtomicString& formEnctypeAttr = attributeWithoutSynchronization(formenctypeAttr);
86 if (formEnctypeAttr.isNull())
87 return emptyString();
88 return FormSubmission::Attributes::parseEncodingType(formEnctypeAttr);
89}
90
91void HTMLFormControlElement::setFormEnctype(const String& value)
92{
93 setAttributeWithoutSynchronization(formenctypeAttr, value);
94}
95
96String HTMLFormControlElement::formMethod() const
97{
98 auto& formMethodAttr = attributeWithoutSynchronization(formmethodAttr);
99 if (formMethodAttr.isNull())
100 return emptyString();
101 return FormSubmission::Attributes::methodString(FormSubmission::Attributes::parseMethodType(formMethodAttr));
102}
103
104void HTMLFormControlElement::setFormMethod(const String& value)
105{
106 setAttributeWithoutSynchronization(formmethodAttr, value);
107}
108
109bool HTMLFormControlElement::formNoValidate() const
110{
111 return hasAttributeWithoutSynchronization(formnovalidateAttr);
112}
113
114String HTMLFormControlElement::formAction() const
115{
116 const AtomicString& value = attributeWithoutSynchronization(formactionAttr);
117 if (value.isEmpty())
118 return document().url();
119 return getURLAttribute(formactionAttr);
120}
121
122void HTMLFormControlElement::setFormAction(const AtomicString& value)
123{
124 setAttributeWithoutSynchronization(formactionAttr, value);
125}
126
127bool HTMLFormControlElement::computeIsDisabledByFieldsetAncestor() const
128{
129 RefPtr<Element> previousAncestor;
130 for (RefPtr<Element> ancestor = parentElement(); ancestor; ancestor = ancestor->parentElement()) {
131 if (is<HTMLFieldSetElement>(*ancestor) && ancestor->hasAttributeWithoutSynchronization(disabledAttr)) {
132 HTMLFieldSetElement& fieldSetAncestor = downcast<HTMLFieldSetElement>(*ancestor);
133 bool isInFirstLegend = is<HTMLLegendElement>(previousAncestor) && previousAncestor == fieldSetAncestor.legend();
134 return !isInFirstLegend;
135 }
136 previousAncestor = ancestor;
137 }
138 return false;
139}
140
141void HTMLFormControlElement::setAncestorDisabled(bool isDisabled)
142{
143 ASSERT(computeIsDisabledByFieldsetAncestor() == isDisabled);
144 bool oldValue = m_disabledByAncestorFieldset;
145 m_disabledByAncestorFieldset = isDisabled;
146 if (oldValue != m_disabledByAncestorFieldset)
147 disabledStateChanged();
148}
149
150void HTMLFormControlElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
151{
152 if (name == formAttr)
153 formAttributeChanged();
154 else if (name == disabledAttr) {
155 if (canBeActuallyDisabled()) {
156 bool oldDisabled = m_disabled;
157 m_disabled = !value.isNull();
158 if (oldDisabled != m_disabled)
159 disabledAttributeChanged();
160 }
161 } else if (name == readonlyAttr) {
162 bool wasReadOnly = m_isReadOnly;
163 m_isReadOnly = !value.isNull();
164 if (wasReadOnly != m_isReadOnly)
165 readOnlyStateChanged();
166 } else if (name == requiredAttr) {
167 bool wasRequired = m_isRequired;
168 m_isRequired = !value.isNull();
169 if (wasRequired != m_isRequired)
170 requiredStateChanged();
171 } else
172 HTMLElement::parseAttribute(name, value);
173}
174
175void HTMLFormControlElement::disabledAttributeChanged()
176{
177 disabledStateChanged();
178}
179
180void HTMLFormControlElement::disabledStateChanged()
181{
182 setNeedsWillValidateCheck();
183 invalidateStyleForSubtree();
184 if (renderer() && renderer()->style().hasAppearance())
185 renderer()->theme().stateChanged(*renderer(), ControlStates::EnabledState);
186}
187
188void HTMLFormControlElement::readOnlyStateChanged()
189{
190 setNeedsWillValidateCheck();
191 invalidateStyleForSubtree();
192}
193
194void HTMLFormControlElement::requiredStateChanged()
195{
196 updateValidity();
197 // Style recalculation is needed because style selectors may include
198 // :required and :optional pseudo-classes.
199 invalidateStyleForSubtree();
200}
201
202static bool shouldAutofocus(HTMLFormControlElement* element)
203{
204 if (!element->renderer())
205 return false;
206 if (!element->hasAttributeWithoutSynchronization(autofocusAttr))
207 return false;
208 if (!element->isConnected() || !element->document().renderView())
209 return false;
210 if (element->document().isSandboxed(SandboxAutomaticFeatures)) {
211 // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
212 element->document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Blocked autofocusing on a form control because the form's frame is sandboxed and the 'allow-scripts' permission is not set."_s);
213 return false;
214 }
215 if (element->hasAutofocused())
216 return false;
217
218 // FIXME: Should this set of hasTagName checks be replaced by a
219 // virtual member function?
220 if (is<HTMLInputElement>(*element))
221 return !downcast<HTMLInputElement>(*element).isInputTypeHidden();
222 if (element->hasTagName(selectTag))
223 return true;
224 if (element->hasTagName(keygenTag))
225 return true;
226 if (element->hasTagName(buttonTag))
227 return true;
228 if (is<HTMLTextAreaElement>(*element))
229 return true;
230
231 return false;
232}
233
234void HTMLFormControlElement::didAttachRenderers()
235{
236 // The call to updateFromElement() needs to go after the call through
237 // to the base class's attach() because that can sometimes do a close
238 // on the renderer.
239 if (renderer())
240 renderer()->updateFromElement();
241
242 if (shouldAutofocus(this)) {
243 setAutofocused();
244
245 RefPtr<HTMLFormControlElement> element = this;
246 auto frameView = makeRefPtr(document().view());
247 if (frameView && frameView->layoutContext().isInLayout()) {
248 frameView->queuePostLayoutCallback([element] {
249 element->focus();
250 });
251 } else {
252 Style::queuePostResolutionCallback([element] {
253 element->focus();
254 });
255 }
256 }
257}
258
259void HTMLFormControlElement::didMoveToNewDocument(Document& oldDocument, Document& newDocument)
260{
261 FormAssociatedElement::didMoveToNewDocument(oldDocument);
262 HTMLElement::didMoveToNewDocument(oldDocument, newDocument);
263}
264
265static void addInvalidElementToAncestorFromInsertionPoint(const HTMLFormControlElement& element, ContainerNode* insertionPoint)
266{
267 if (!is<Element>(insertionPoint))
268 return;
269
270 for (auto& ancestor : lineageOfType<HTMLFieldSetElement>(downcast<Element>(*insertionPoint)))
271 ancestor.addInvalidDescendant(element);
272}
273
274static void removeInvalidElementToAncestorFromInsertionPoint(const HTMLFormControlElement& element, ContainerNode* insertionPoint)
275{
276 if (!is<Element>(insertionPoint))
277 return;
278
279 for (auto& ancestor : lineageOfType<HTMLFieldSetElement>(downcast<Element>(*insertionPoint)))
280 ancestor.removeInvalidDescendant(element);
281}
282
283Node::InsertedIntoAncestorResult HTMLFormControlElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
284{
285 if (m_dataListAncestorState == NotInsideDataList)
286 m_dataListAncestorState = Unknown;
287
288 setNeedsWillValidateCheck();
289 if (willValidate() && !isValidFormControlElement())
290 addInvalidElementToAncestorFromInsertionPoint(*this, &parentOfInsertedTree);
291 if (document().hasDisabledFieldsetElement())
292 setAncestorDisabled(computeIsDisabledByFieldsetAncestor());
293 HTMLElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
294 FormAssociatedElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
295 return InsertedIntoAncestorResult::NeedsPostInsertionCallback;
296}
297
298void HTMLFormControlElement::didFinishInsertingNode()
299{
300 resetFormOwner();
301}
302
303void HTMLFormControlElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
304{
305 bool wasMatchingInvalidPseudoClass = willValidate() && !isValidFormControlElement();
306
307 m_validationMessage = nullptr;
308 if (m_disabledByAncestorFieldset)
309 setAncestorDisabled(computeIsDisabledByFieldsetAncestor());
310
311 bool wasInsideDataList = false;
312 if (m_dataListAncestorState == InsideDataList) {
313 m_dataListAncestorState = Unknown;
314 wasInsideDataList = true;
315 }
316
317 HTMLElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
318 FormAssociatedElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
319
320 if (wasMatchingInvalidPseudoClass)
321 removeInvalidElementToAncestorFromInsertionPoint(*this, &oldParentOfRemovedTree);
322
323 if (wasInsideDataList)
324 setNeedsWillValidateCheck();
325}
326
327void HTMLFormControlElement::setChangedSinceLastFormControlChangeEvent(bool changed)
328{
329 m_wasChangedSinceLastFormControlChangeEvent = changed;
330}
331
332void HTMLFormControlElement::dispatchChangeEvent()
333{
334 dispatchScopedEvent(Event::create(eventNames().changeEvent, Event::CanBubble::Yes, Event::IsCancelable::No));
335}
336
337void HTMLFormControlElement::dispatchFormControlChangeEvent()
338{
339 dispatchChangeEvent();
340 setChangedSinceLastFormControlChangeEvent(false);
341}
342
343void HTMLFormControlElement::dispatchFormControlInputEvent()
344{
345 setChangedSinceLastFormControlChangeEvent(true);
346 dispatchInputEvent();
347}
348
349bool HTMLFormControlElement::isDisabledFormControl() const
350{
351 return m_disabled || m_disabledByAncestorFieldset;
352}
353
354bool HTMLFormControlElement::isRequired() const
355{
356 return m_isRequired;
357}
358
359void HTMLFormControlElement::didRecalcStyle(Style::Change)
360{
361 // updateFromElement() can cause the selection to change, and in turn
362 // trigger synchronous layout, so it must not be called during style recalc.
363 if (renderer()) {
364 RefPtr<HTMLFormControlElement> element = this;
365 Style::queuePostResolutionCallback([element]{
366 if (auto* renderer = element->renderer())
367 renderer->updateFromElement();
368 });
369 }
370}
371
372bool HTMLFormControlElement::supportsFocus() const
373{
374 return !isDisabledFormControl();
375}
376
377bool HTMLFormControlElement::isKeyboardFocusable(KeyboardEvent* event) const
378{
379 return isFocusable()
380 && document().frame()
381 && document().frame()->eventHandler().tabsToAllFormControls(event);
382}
383
384bool HTMLFormControlElement::isMouseFocusable() const
385{
386#if PLATFORM(GTK)
387 return HTMLElement::isMouseFocusable();
388#else
389 if (needsMouseFocusableQuirk())
390 return HTMLElement::isMouseFocusable();
391 return false;
392#endif
393}
394
395bool HTMLFormControlElement::matchesValidPseudoClass() const
396{
397 return willValidate() && isValidFormControlElement();
398}
399
400bool HTMLFormControlElement::matchesInvalidPseudoClass() const
401{
402 return willValidate() && !isValidFormControlElement();
403}
404
405int HTMLFormControlElement::tabIndex() const
406{
407 // Skip the supportsFocus check in HTMLElement.
408 return Element::tabIndex();
409}
410
411bool HTMLFormControlElement::computeWillValidate() const
412{
413 if (m_dataListAncestorState == Unknown) {
414 for (ContainerNode* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) {
415 if (ancestor->hasTagName(datalistTag)) {
416 m_dataListAncestorState = InsideDataList;
417 break;
418 }
419 }
420 if (m_dataListAncestorState == Unknown)
421 m_dataListAncestorState = NotInsideDataList;
422 }
423 return m_dataListAncestorState == NotInsideDataList && !isDisabledOrReadOnly();
424}
425
426bool HTMLFormControlElement::willValidate() const
427{
428 if (!m_willValidateInitialized || m_dataListAncestorState == Unknown) {
429 m_willValidateInitialized = true;
430 bool newWillValidate = computeWillValidate();
431 if (m_willValidate != newWillValidate)
432 m_willValidate = newWillValidate;
433 } else {
434 // If the following assertion fails, setNeedsWillValidateCheck() is not
435 // called correctly when something which changes computeWillValidate() result
436 // is updated.
437 ASSERT(m_willValidate == computeWillValidate());
438 }
439 return m_willValidate;
440}
441
442void HTMLFormControlElement::setNeedsWillValidateCheck()
443{
444 // We need to recalculate willValidate immediately because willValidate change can causes style change.
445 bool newWillValidate = computeWillValidate();
446 if (m_willValidateInitialized && m_willValidate == newWillValidate)
447 return;
448
449 bool wasValid = m_isValid;
450
451 m_willValidateInitialized = true;
452 m_willValidate = newWillValidate;
453
454 updateValidity();
455 invalidateStyleForSubtree();
456
457 if (!m_willValidate && !wasValid) {
458 removeInvalidElementToAncestorFromInsertionPoint(*this, parentNode());
459 if (RefPtr<HTMLFormElement> form = this->form())
460 form->removeInvalidAssociatedFormControlIfNeeded(*this);
461 }
462
463 if (!m_willValidate)
464 hideVisibleValidationMessage();
465}
466
467void HTMLFormControlElement::updateVisibleValidationMessage()
468{
469 Page* page = document().page();
470 if (!page)
471 return;
472 String message;
473 if (renderer() && willValidate())
474 message = validationMessage().stripWhiteSpace();
475 if (!m_validationMessage)
476 m_validationMessage = std::make_unique<ValidationMessage>(this);
477 m_validationMessage->updateValidationMessage(message);
478}
479
480void HTMLFormControlElement::hideVisibleValidationMessage()
481{
482 if (m_validationMessage)
483 m_validationMessage->requestToHideMessage();
484}
485
486bool HTMLFormControlElement::checkValidity(Vector<RefPtr<HTMLFormControlElement>>* unhandledInvalidControls)
487{
488 if (!willValidate() || isValidFormControlElement())
489 return true;
490 // An event handler can deref this object.
491 Ref<HTMLFormControlElement> protectedThis(*this);
492 Ref<Document> originalDocument(document());
493 auto event = Event::create(eventNames().invalidEvent, Event::CanBubble::No, Event::IsCancelable::Yes);
494 dispatchEvent(event);
495 if (!event->defaultPrevented() && unhandledInvalidControls && isConnected() && originalDocument.ptr() == &document())
496 unhandledInvalidControls->append(this);
497 return false;
498}
499
500bool HTMLFormControlElement::isShowingValidationMessage() const
501{
502 return m_validationMessage && m_validationMessage->isVisible();
503}
504
505bool HTMLFormControlElement::reportValidity()
506{
507 Vector<RefPtr<HTMLFormControlElement>> elements;
508 if (checkValidity(&elements))
509 return true;
510
511 if (elements.isEmpty())
512 return false;
513
514 // Needs to update layout now because we'd like to call isFocusable(), which
515 // has !renderer()->needsLayout() assertion.
516 document().updateLayoutIgnorePendingStylesheets();
517
518 if (isConnected() && isFocusable()) {
519 focusAndShowValidationMessage();
520 return false;
521 }
522
523 if (document().frame()) {
524 String message = makeString("An invalid form control with name='", name(), "' is not focusable.");
525 document().addConsoleMessage(MessageSource::Rendering, MessageLevel::Error, message);
526 }
527
528 return false;
529}
530
531void HTMLFormControlElement::focusAndShowValidationMessage()
532{
533 // Calling focus() will scroll the element into view.
534 focus();
535
536 // focus() will scroll the element into view and this scroll may happen asynchronously.
537 // Because scrolling the view hides the validation message, we need to show the validation
538 // message asynchronously as well.
539 callOnMainThread([this, protectedThis = makeRef(*this)] {
540 updateVisibleValidationMessage();
541 });
542}
543
544inline bool HTMLFormControlElement::isValidFormControlElement() const
545{
546 // If the following assertion fails, updateValidity() is not called
547 // correctly when something which changes validity is updated.
548 ASSERT(m_isValid == isValid());
549 return m_isValid;
550}
551
552void HTMLFormControlElement::willChangeForm()
553{
554 if (HTMLFormElement* form = this->form())
555 form->removeInvalidAssociatedFormControlIfNeeded(*this);
556 FormAssociatedElement::willChangeForm();
557}
558
559void HTMLFormControlElement::didChangeForm()
560{
561 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
562
563 FormAssociatedElement::didChangeForm();
564 if (auto* form = this->form()) {
565 if (m_willValidateInitialized && m_willValidate && !isValidFormControlElement())
566 form->registerInvalidAssociatedFormControl(*this);
567 }
568}
569
570void HTMLFormControlElement::updateValidity()
571{
572 bool willValidate = this->willValidate();
573 bool wasValid = m_isValid;
574
575 m_isValid = isValid();
576
577 if (willValidate && m_isValid != wasValid) {
578 // Update style for pseudo classes such as :valid :invalid.
579 invalidateStyleForSubtree();
580
581 if (!m_isValid) {
582 addInvalidElementToAncestorFromInsertionPoint(*this, parentNode());
583 if (HTMLFormElement* form = this->form())
584 form->registerInvalidAssociatedFormControl(*this);
585 } else {
586 removeInvalidElementToAncestorFromInsertionPoint(*this, parentNode());
587 if (HTMLFormElement* form = this->form())
588 form->removeInvalidAssociatedFormControlIfNeeded(*this);
589 }
590 }
591
592 // Updates only if this control already has a validtion message.
593 if (m_validationMessage && m_validationMessage->isVisible()) {
594 // Calls updateVisibleValidationMessage() even if m_isValid is not
595 // changed because a validation message can be chagned.
596 updateVisibleValidationMessage();
597 }
598}
599
600void HTMLFormControlElement::setCustomValidity(const String& error)
601{
602 FormAssociatedElement::setCustomValidity(error);
603 updateValidity();
604}
605
606bool HTMLFormControlElement::validationMessageShadowTreeContains(const Node& node) const
607{
608 return m_validationMessage && m_validationMessage->shadowTreeContains(node);
609}
610
611void HTMLFormControlElement::dispatchBlurEvent(RefPtr<Element>&& newFocusedElement)
612{
613 HTMLElement::dispatchBlurEvent(WTFMove(newFocusedElement));
614 hideVisibleValidationMessage();
615}
616
617#if ENABLE(IOS_AUTOCORRECT_AND_AUTOCAPITALIZE)
618
619// FIXME: We should look to share this code with class HTMLFormElement instead of duplicating the logic.
620
621bool HTMLFormControlElement::shouldAutocorrect() const
622{
623 const AtomicString& autocorrectValue = attributeWithoutSynchronization(autocorrectAttr);
624 if (!autocorrectValue.isEmpty())
625 return !equalLettersIgnoringASCIICase(autocorrectValue, "off");
626 if (RefPtr<HTMLFormElement> form = this->form())
627 return form->shouldAutocorrect();
628 return true;
629}
630
631AutocapitalizeType HTMLFormControlElement::autocapitalizeType() const
632{
633 AutocapitalizeType type = HTMLElement::autocapitalizeType();
634 if (type == AutocapitalizeTypeDefault) {
635 if (RefPtr<HTMLFormElement> form = this->form())
636 return form->autocapitalizeType();
637 }
638 return type;
639}
640
641#endif
642
643HTMLFormControlElement* HTMLFormControlElement::enclosingFormControlElement(Node* node)
644{
645 for (; node; node = node->parentNode()) {
646 if (is<HTMLFormControlElement>(*node))
647 return downcast<HTMLFormControlElement>(node);
648 }
649 return nullptr;
650}
651
652String HTMLFormControlElement::autocomplete() const
653{
654 return autofillData().idlExposedValue;
655}
656
657void HTMLFormControlElement::setAutocomplete(const String& value)
658{
659 setAttributeWithoutSynchronization(autocompleteAttr, value);
660}
661
662AutofillMantle HTMLFormControlElement::autofillMantle() const
663{
664 return is<HTMLInputElement>(*this) && downcast<HTMLInputElement>(this)->isInputTypeHidden() ? AutofillMantle::Anchor : AutofillMantle::Expectation;
665}
666
667AutofillData HTMLFormControlElement::autofillData() const
668{
669 // FIXME: We could cache the AutofillData if we we had an efficient way to invalidate the cache when
670 // the autofill mantle changed (due to a type change on an <input> element) or the element's form
671 // owner's autocomplete attribute changed or the form owner itself changed.
672
673 return AutofillData::createFromHTMLFormControlElement(*this);
674}
675
676// FIXME: We should remove the quirk once <rdar://problem/47334655> is fixed.
677bool HTMLFormControlElement::needsMouseFocusableQuirk() const
678{
679 return document().quirks().needsFormControlToBeMouseFocusable();
680}
681
682} // namespace Webcore
683