Add support for reportValidity() on form and form control elements
[WebKit-https.git] / Source / WebCore / html / HTMLFormElement.cpp
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, 2008, 2009 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 "HTMLFormElement.h"
27
28 #include "AutocompleteErrorEvent.h"
29 #include "DOMFormData.h"
30 #include "DOMWindow.h"
31 #include "Document.h"
32 #include "ElementIterator.h"
33 #include "Event.h"
34 #include "EventNames.h"
35 #include "FormController.h"
36 #include "FormData.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "FrameLoaderClient.h"
40 #include "HTMLFieldSetElement.h"
41 #include "HTMLFormControlsCollection.h"
42 #include "HTMLImageElement.h"
43 #include "HTMLInputElement.h"
44 #include "HTMLNames.h"
45 #include "HTMLObjectElement.h"
46 #include "HTMLTableElement.h"
47 #include "NodeRareData.h"
48 #include "Page.h"
49 #include "RenderTextControl.h"
50 #include "ScriptController.h"
51 #include "Settings.h"
52 #include <limits>
53 #include <wtf/Ref.h>
54
55 namespace WebCore {
56
57 using namespace HTMLNames;
58
59 HTMLFormElement::HTMLFormElement(const QualifiedName& tagName, Document& document)
60     : HTMLElement(tagName, document)
61     , m_associatedElementsBeforeIndex(0)
62     , m_associatedElementsAfterIndex(0)
63     , m_wasUserSubmitted(false)
64     , m_isSubmittingOrPreparingForSubmission(false)
65     , m_shouldSubmit(false)
66     , m_isInResetFunction(false)
67     , m_wasDemoted(false)
68 #if ENABLE(REQUEST_AUTOCOMPLETE)
69     , m_requestAutocompletetimer(*this, &HTMLFormElement::requestAutocompleteTimerFired)
70 #endif
71 {
72     ASSERT(hasTagName(formTag));
73 }
74
75 Ref<HTMLFormElement> HTMLFormElement::create(Document& document)
76 {
77     return adoptRef(*new HTMLFormElement(formTag, document));
78 }
79
80 Ref<HTMLFormElement> HTMLFormElement::create(const QualifiedName& tagName, Document& document)
81 {
82     return adoptRef(*new HTMLFormElement(tagName, document));
83 }
84
85 HTMLFormElement::~HTMLFormElement()
86 {
87     document().formController().willDeleteForm(this);
88     if (!shouldAutocomplete())
89         document().unregisterForDocumentSuspensionCallbacks(this);
90
91     m_defaultButton = nullptr;
92     for (auto& associatedElement : m_associatedElements)
93         associatedElement->formWillBeDestroyed();
94     for (auto& imageElement : m_imageElements)
95         imageElement->m_form = nullptr;
96 }
97
98 bool HTMLFormElement::formWouldHaveSecureSubmission(const String& url)
99 {
100     return document().completeURL(url).protocolIs("https");
101 }
102
103 bool HTMLFormElement::rendererIsNeeded(const RenderStyle& style)
104 {
105     if (!m_wasDemoted)
106         return HTMLElement::rendererIsNeeded(style);
107
108     auto parent = parentNode();
109     auto parentRenderer = parent->renderer();
110
111     if (!parentRenderer)
112         return false;
113
114     // FIXME: Shouldn't we also check for table caption (see |formIsTablePart| below).
115     bool parentIsTableElementPart = (parentRenderer->isTable() && is<HTMLTableElement>(*parent))
116         || (parentRenderer->isTableRow() && parent->hasTagName(trTag))
117         || (parentRenderer->isTableSection() && parent->hasTagName(tbodyTag))
118         || (parentRenderer->isRenderTableCol() && parent->hasTagName(colTag))
119         || (parentRenderer->isTableCell() && parent->hasTagName(trTag));
120
121     if (!parentIsTableElementPart)
122         return true;
123
124     EDisplay display = style.display();
125     bool formIsTablePart = display == TABLE || display == INLINE_TABLE || display == TABLE_ROW_GROUP
126         || display == TABLE_HEADER_GROUP || display == TABLE_FOOTER_GROUP || display == TABLE_ROW
127         || display == TABLE_COLUMN_GROUP || display == TABLE_COLUMN || display == TABLE_CELL
128         || display == TABLE_CAPTION;
129
130     return formIsTablePart;
131 }
132
133 Node::InsertionNotificationRequest HTMLFormElement::insertedInto(ContainerNode& insertionPoint)
134 {
135     HTMLElement::insertedInto(insertionPoint);
136     if (insertionPoint.inDocument())
137         document().didAssociateFormControl(this);
138     return InsertionDone;
139 }
140
141 static inline Node* findRoot(Node* n)
142 {
143     Node* root = n;
144     for (; n; n = n->parentNode())
145         root = n;
146     return root;
147 }
148
149 void HTMLFormElement::removedFrom(ContainerNode& insertionPoint)
150 {
151     Node* root = findRoot(this);
152     Vector<FormAssociatedElement*> associatedElements(m_associatedElements);
153     for (auto& associatedElement : associatedElements)
154         associatedElement->formRemovedFromTree(root);
155     HTMLElement::removedFrom(insertionPoint);
156 }
157
158 void HTMLFormElement::handleLocalEvents(Event& event)
159 {
160     Node* targetNode = event.target()->toNode();
161     if (event.eventPhase() != Event::CAPTURING_PHASE && targetNode && targetNode != this && (event.type() == eventNames().submitEvent || event.type() == eventNames().resetEvent)) {
162         event.stopPropagation();
163         return;
164     }
165     HTMLElement::handleLocalEvents(event);
166 }
167
168 unsigned HTMLFormElement::length() const
169 {
170     unsigned len = 0;
171     for (auto& associatedElement : m_associatedElements) {
172         if (associatedElement->isEnumeratable())
173             ++len;
174     }
175     return len;
176 }
177
178 HTMLElement* HTMLFormElement::item(unsigned index)
179 {
180     return elements()->item(index);
181 }
182
183 void HTMLFormElement::submitImplicitly(Event& event, bool fromImplicitSubmissionTrigger)
184 {
185     unsigned submissionTriggerCount = 0;
186     for (auto& formAssociatedElement : m_associatedElements) {
187         if (!is<HTMLFormControlElement>(*formAssociatedElement))
188             continue;
189         HTMLFormControlElement& formElement = downcast<HTMLFormControlElement>(*formAssociatedElement);
190         if (formElement.isSuccessfulSubmitButton()) {
191             if (formElement.renderer()) {
192                 formElement.dispatchSimulatedClick(&event);
193                 return;
194             }
195         } else if (formElement.canTriggerImplicitSubmission())
196             ++submissionTriggerCount;
197     }
198
199     if (!submissionTriggerCount)
200         return;
201
202     // Older iOS apps using WebViews expect the behavior of auto submitting multi-input forms.
203     Settings* settings = document().settings();
204     if (fromImplicitSubmissionTrigger && (submissionTriggerCount == 1 || (settings && settings->allowMultiElementImplicitSubmission())))
205         prepareForSubmission(event);
206 }
207
208 static inline HTMLFormControlElement* submitElementFromEvent(const Event& event)
209 {
210     for (Node* node = event.target()->toNode(); node; node = node->parentNode()) {
211         if (is<HTMLFormControlElement>(*node))
212             return downcast<HTMLFormControlElement>(node);
213     }
214     return nullptr;
215 }
216
217 bool HTMLFormElement::validateInteractively()
218 {
219     for (auto& associatedElement : m_associatedElements) {
220         if (is<HTMLFormControlElement>(*associatedElement))
221             downcast<HTMLFormControlElement>(*associatedElement).hideVisibleValidationMessage();
222     }
223
224     Vector<RefPtr<HTMLFormControlElement>> unhandledInvalidControls;
225     if (!checkInvalidControlsAndCollectUnhandled(unhandledInvalidControls))
226         return true;
227     // Because the form has invalid controls, we abort the form submission and
228     // show a validation message on a focusable form control.
229
230     // Needs to update layout now because we'd like to call isFocusable(), which
231     // has !renderer()->needsLayout() assertion.
232     document().updateLayoutIgnorePendingStylesheets();
233
234     Ref<HTMLFormElement> protectedThis(*this);
235
236     // Focus on the first focusable control and show a validation message.
237     for (auto& control : unhandledInvalidControls) {
238         if (control->inDocument() && control->isFocusable()) {
239             control->focusAndShowValidationMessage();
240             break;
241         }
242     }
243
244     // Warn about all of unfocusable controls.
245     if (document().frame()) {
246         for (auto& control : unhandledInvalidControls) {
247             if (control->inDocument() && control->isFocusable())
248                 continue;
249             String message = makeString("An invalid form control with name='", control->name(), "' is not focusable.");
250             document().addConsoleMessage(MessageSource::Rendering, MessageLevel::Error, message);
251         }
252     }
253
254     return false;
255 }
256
257 void HTMLFormElement::prepareForSubmission(Event& event)
258 {
259     Frame* frame = document().frame();
260     if (m_isSubmittingOrPreparingForSubmission || !frame)
261         return;
262
263     m_isSubmittingOrPreparingForSubmission = true;
264     m_shouldSubmit = false;
265
266     bool shouldValidate = document().page() && document().page()->settings().interactiveFormValidationEnabled() && !noValidate();
267
268     HTMLFormControlElement* submitElement = submitElementFromEvent(event);
269     if (submitElement && submitElement->formNoValidate())
270         shouldValidate = false;
271
272     // Interactive validation must be done before dispatching the submit event.
273     if (shouldValidate && !validateInteractively()) {
274         m_isSubmittingOrPreparingForSubmission = false;
275         return;
276     }
277
278     StringPairVector controlNamesAndValues;
279     getTextFieldValues(controlNamesAndValues);
280     auto formState = FormState::create(this, controlNamesAndValues, &document(), NotSubmittedByJavaScript);
281     frame->loader().client().dispatchWillSendSubmitEvent(WTFMove(formState));
282
283     Ref<HTMLFormElement> protectedThis(*this);
284     // Event handling can result in m_shouldSubmit becoming true, regardless of dispatchEvent() return value.
285     if (dispatchEvent(Event::create(eventNames().submitEvent, true, true)))
286         m_shouldSubmit = true;
287
288     m_isSubmittingOrPreparingForSubmission = false;
289
290     if (m_shouldSubmit)
291         submit(&event, true, true, NotSubmittedByJavaScript);
292 }
293
294 void HTMLFormElement::submit()
295 {
296     submit(0, false, true, NotSubmittedByJavaScript);
297 }
298
299 void HTMLFormElement::submitFromJavaScript()
300 {
301     submit(0, false, ScriptController::processingUserGesture(), SubmittedByJavaScript);
302 }
303
304 void HTMLFormElement::getTextFieldValues(StringPairVector& fieldNamesAndValues) const
305 {
306     ASSERT_ARG(fieldNamesAndValues, fieldNamesAndValues.isEmpty());
307
308     fieldNamesAndValues.reserveCapacity(m_associatedElements.size());
309     for (auto& associatedElement : m_associatedElements) {
310         HTMLElement& element = associatedElement->asHTMLElement();
311         if (!is<HTMLInputElement>(element))
312             continue;
313         HTMLInputElement& input = downcast<HTMLInputElement>(element);
314         if (!input.isTextField())
315             continue;
316         fieldNamesAndValues.append(std::make_pair(input.name().string(), input.value()));
317     }
318 }
319
320 void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool processingUserGesture, FormSubmissionTrigger formSubmissionTrigger)
321 {
322     FrameView* view = document().view();
323     Frame* frame = document().frame();
324     if (!view || !frame)
325         return;
326
327     if (m_isSubmittingOrPreparingForSubmission) {
328         m_shouldSubmit = true;
329         return;
330     }
331
332     m_isSubmittingOrPreparingForSubmission = true;
333     m_wasUserSubmitted = processingUserGesture;
334
335     RefPtr<HTMLFormControlElement> firstSuccessfulSubmitButton;
336     bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button?
337
338     for (auto& associatedElement : m_associatedElements) {
339         if (!is<HTMLFormControlElement>(*associatedElement))
340             continue;
341         if (needButtonActivation) {
342             HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*associatedElement);
343             if (control.isActivatedSubmit())
344                 needButtonActivation = false;
345             else if (!firstSuccessfulSubmitButton && control.isSuccessfulSubmitButton())
346                 firstSuccessfulSubmitButton = &control;
347         }
348     }
349
350     if (needButtonActivation && firstSuccessfulSubmitButton)
351         firstSuccessfulSubmitButton->setActivatedSubmit(true);
352
353     LockHistory lockHistory = processingUserGesture ? LockHistory::No : LockHistory::Yes;
354     Ref<HTMLFormElement> protectedThis(*this); // Form submission can execute arbitary JavaScript.
355     frame->loader().submitForm(FormSubmission::create(this, m_attributes, event, lockHistory, formSubmissionTrigger));
356
357     if (needButtonActivation && firstSuccessfulSubmitButton)
358         firstSuccessfulSubmitButton->setActivatedSubmit(false);
359
360     m_shouldSubmit = false;
361     m_isSubmittingOrPreparingForSubmission = false;
362 }
363
364 void HTMLFormElement::reset()
365 {
366     Frame* frame = document().frame();
367     if (m_isInResetFunction || !frame)
368         return;
369
370     m_isInResetFunction = true;
371
372     if (!dispatchEvent(Event::create(eventNames().resetEvent, true, true))) {
373         m_isInResetFunction = false;
374         return;
375     }
376
377     for (auto& associatedElement : m_associatedElements) {
378         if (is<HTMLFormControlElement>(*associatedElement))
379             downcast<HTMLFormControlElement>(*associatedElement).reset();
380     }
381
382     m_isInResetFunction = false;
383 }
384
385 #if ENABLE(IOS_AUTOCORRECT_AND_AUTOCAPITALIZE)
386
387 // FIXME: We should look to share this code with class HTMLFormControlElement instead of duplicating the logic.
388
389 bool HTMLFormElement::autocorrect() const
390 {
391     const AtomicString& autocorrectValue = attributeWithoutSynchronization(autocorrectAttr);
392     if (!autocorrectValue.isEmpty())
393         return !equalLettersIgnoringASCIICase(autocorrectValue, "off");
394     if (HTMLFormElement* form = this->form())
395         return form->autocorrect();
396     return true;
397 }
398
399 void HTMLFormElement::setAutocorrect(bool autocorrect)
400 {
401     setAttributeWithoutSynchronization(autocorrectAttr, autocorrect ? AtomicString("on", AtomicString::ConstructFromLiteral) : AtomicString("off", AtomicString::ConstructFromLiteral));
402 }
403
404 WebAutocapitalizeType HTMLFormElement::autocapitalizeType() const
405 {
406     return autocapitalizeTypeForAttributeValue(attributeWithoutSynchronization(autocapitalizeAttr));
407 }
408
409 const AtomicString& HTMLFormElement::autocapitalize() const
410 {
411     return stringForAutocapitalizeType(autocapitalizeType());
412 }
413
414 void HTMLFormElement::setAutocapitalize(const AtomicString& value)
415 {
416     setAttributeWithoutSynchronization(autocapitalizeAttr, value);
417 }
418
419 #endif
420
421 #if ENABLE(REQUEST_AUTOCOMPLETE)
422
423 void HTMLFormElement::requestAutocomplete()
424 {
425     Frame* frame = document().frame();
426     if (!frame)
427         return;
428
429     if (!shouldAutocomplete() || !ScriptController::processingUserGesture()) {
430         finishRequestAutocomplete(AutocompleteResult::ErrorDisabled);
431         return;
432     }
433
434     StringPairVector controlNamesAndValues;
435     getTextFieldValues(controlNamesAndValues);
436
437     RefPtr<FormState> formState = FormState::create(this, controlNamesAndValues, &document(), SubmittedByJavaScript);
438     frame->loader().client().didRequestAutocomplete(formState.release());
439 }
440
441 void HTMLFormElement::finishRequestAutocomplete(AutocompleteResult result)
442 {
443     RefPtr<Event> event;
444     switch (result) {
445     case AutocompleteResult::Success:
446         event = Event::create(eventNames().autocompleteEvent, false, false);
447         break;
448     case AutocompleteResult::ErrorDisabled:
449         event = AutocompleteErrorEvent::create("disabled");
450         break;
451     case AutocompleteResult::ErrorCancel:
452         event = AutocompleteErrorEvent::create("cancel");
453         break;
454     case AutocompleteResult::ErrorInvalid:
455         event = AutocompleteErrorEvent::create("invalid");
456         break;
457     }
458
459     event->setTarget(this);
460     m_pendingAutocompleteEvents.append(WTFMove(event));
461
462     // Dispatch events later as this API is meant to work asynchronously in all situations and implementations.
463     if (!m_requestAutocompleteTimer.isActive())
464         m_requestAutocompleteTimer.startOneShot(0);
465 }
466
467 void HTMLFormElement::requestAutocompleteTimerFired()
468 {
469     Vector<RefPtr<Event>> pendingEvents;
470     m_pendingAutocompleteEvents.swap(pendingEvents);
471     for (auto& pendingEvent : pendingEvents)
472         dispatchEvent(pendingEvent.release());
473 }
474
475 #endif
476
477 void HTMLFormElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
478 {
479     if (name == actionAttr) {
480         m_attributes.parseAction(value);
481         
482         if (!m_attributes.action().isEmpty()) {
483             if (Frame* f = document().frame()) {
484                 Frame& topFrame = f->tree().top();
485                 topFrame.loader().mixedContentChecker().checkFormForMixedContent(topFrame.document()->securityOrigin(), document().completeURL(m_attributes.action()));
486             }
487         }
488     } else if (name == targetAttr)
489         m_attributes.setTarget(value);
490     else if (name == methodAttr)
491         m_attributes.updateMethodType(value);
492     else if (name == enctypeAttr)
493         m_attributes.updateEncodingType(value);
494     else if (name == accept_charsetAttr)
495         m_attributes.setAcceptCharset(value);
496     else if (name == autocompleteAttr) {
497         if (!shouldAutocomplete())
498             document().registerForDocumentSuspensionCallbacks(this);
499         else
500             document().unregisterForDocumentSuspensionCallbacks(this);
501     } else
502         HTMLElement::parseAttribute(name, value);
503 }
504
505 unsigned HTMLFormElement::formElementIndexWithFormAttribute(Element* element, unsigned rangeStart, unsigned rangeEnd)
506 {
507     if (m_associatedElements.isEmpty())
508         return 0;
509
510     ASSERT(rangeStart <= rangeEnd);
511
512     if (rangeStart == rangeEnd)
513         return rangeStart;
514
515     unsigned left = rangeStart;
516     unsigned right = rangeEnd - 1;
517     unsigned short position;
518
519     // Does binary search on m_associatedElements in order to find the index
520     // to be inserted.
521     while (left != right) {
522         unsigned middle = left + ((right - left) / 2);
523         ASSERT(middle < m_associatedElementsBeforeIndex || middle >= m_associatedElementsAfterIndex);
524         position = element->compareDocumentPosition(m_associatedElements[middle]->asHTMLElement());
525         if (position & DOCUMENT_POSITION_FOLLOWING)
526             right = middle;
527         else
528             left = middle + 1;
529     }
530     
531     ASSERT(left < m_associatedElementsBeforeIndex || left >= m_associatedElementsAfterIndex);
532     position = element->compareDocumentPosition(m_associatedElements[left]->asHTMLElement());
533     if (position & DOCUMENT_POSITION_FOLLOWING)
534         return left;
535     return left + 1;
536 }
537
538 unsigned HTMLFormElement::formElementIndex(FormAssociatedElement* associatedElement)
539 {
540     ASSERT(associatedElement);
541
542     HTMLElement& associatedHTMLElement = associatedElement->asHTMLElement();
543
544     // Treats separately the case where this element has the form attribute
545     // for performance consideration.
546     if (associatedHTMLElement.hasAttributeWithoutSynchronization(formAttr)) {
547         unsigned short position = compareDocumentPosition(associatedHTMLElement);
548         if (position & DOCUMENT_POSITION_PRECEDING) {
549             ++m_associatedElementsBeforeIndex;
550             ++m_associatedElementsAfterIndex;
551             return HTMLFormElement::formElementIndexWithFormAttribute(&associatedHTMLElement, 0, m_associatedElementsBeforeIndex - 1);
552         }
553         if (position & DOCUMENT_POSITION_FOLLOWING && !(position & DOCUMENT_POSITION_CONTAINED_BY))
554             return HTMLFormElement::formElementIndexWithFormAttribute(&associatedHTMLElement, m_associatedElementsAfterIndex, m_associatedElements.size());
555     }
556
557     unsigned currentAssociatedElementsAfterIndex = m_associatedElementsAfterIndex;
558     ++m_associatedElementsAfterIndex;
559
560     if (!associatedHTMLElement.isDescendantOf(this))
561         return currentAssociatedElementsAfterIndex;
562
563     // Check for the special case where this element is the very last thing in
564     // the form's tree of children; we don't want to walk the entire tree in that
565     // common case that occurs during parsing; instead we'll just return a value
566     // that says "add this form element to the end of the array".
567     auto descendants = descendantsOfType<HTMLElement>(*this);
568     auto it = descendants.beginAt(associatedHTMLElement);
569     auto end = descendants.end();
570     if (++it == end)
571         return currentAssociatedElementsAfterIndex;
572
573     unsigned i = m_associatedElementsBeforeIndex;
574     for (auto& element : descendants) {
575         if (&element == &associatedHTMLElement)
576             return i;
577         if (!is<HTMLFormControlElement>(element) && !is<HTMLObjectElement>(element))
578             continue;
579         if (element.form() != this)
580             continue;
581         ++i;
582     }
583     return currentAssociatedElementsAfterIndex;
584 }
585
586 void HTMLFormElement::registerFormElement(FormAssociatedElement* e)
587 {
588     m_associatedElements.insert(formElementIndex(e), e);
589
590     if (is<HTMLFormControlElement>(e)) {
591         HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*e);
592         if (control.isSuccessfulSubmitButton()) {
593             if (!m_defaultButton)
594                 control.setNeedsStyleRecalc();
595             else
596                 resetDefaultButton();
597         }
598     }
599 }
600
601 void HTMLFormElement::removeFormElement(FormAssociatedElement* e)
602 {
603     unsigned index = m_associatedElements.find(e);
604     ASSERT_WITH_SECURITY_IMPLICATION(index < m_associatedElements.size());
605     if (index < m_associatedElementsBeforeIndex)
606         --m_associatedElementsBeforeIndex;
607     if (index < m_associatedElementsAfterIndex)
608         --m_associatedElementsAfterIndex;
609     removeFromPastNamesMap(e);
610     m_associatedElements.remove(index);
611
612     if (e == m_defaultButton)
613         resetDefaultButton();
614 }
615
616 void HTMLFormElement::registerInvalidAssociatedFormControl(const HTMLFormControlElement& formControlElement)
617 {
618     ASSERT_WITH_MESSAGE(!is<HTMLFieldSetElement>(formControlElement), "FieldSet are never candidates for constraint validation.");
619     ASSERT(static_cast<const Element&>(formControlElement).matchesInvalidPseudoClass());
620
621     if (m_invalidAssociatedFormControls.isEmpty())
622         setNeedsStyleRecalc();
623     m_invalidAssociatedFormControls.add(&formControlElement);
624 }
625
626 void HTMLFormElement::removeInvalidAssociatedFormControlIfNeeded(const HTMLFormControlElement& formControlElement)
627 {
628     if (m_invalidAssociatedFormControls.remove(&formControlElement)) {
629         if (m_invalidAssociatedFormControls.isEmpty())
630             setNeedsStyleRecalc();
631     }
632 }
633
634 bool HTMLFormElement::isURLAttribute(const Attribute& attribute) const
635 {
636     return attribute.name() == actionAttr || HTMLElement::isURLAttribute(attribute);
637 }
638
639 void HTMLFormElement::registerImgElement(HTMLImageElement* e)
640 {
641     ASSERT(m_imageElements.find(e) == notFound);
642     m_imageElements.append(e);
643 }
644
645 void HTMLFormElement::removeImgElement(HTMLImageElement* e)
646 {
647     removeFromPastNamesMap(e);
648     bool removed = m_imageElements.removeFirst(e);
649     ASSERT_UNUSED(removed, removed);
650 }
651
652 Ref<HTMLFormControlsCollection> HTMLFormElement::elements()
653 {
654     return ensureRareData().ensureNodeLists().addCachedCollection<HTMLFormControlsCollection>(*this, FormControls);
655 }
656
657 Ref<HTMLCollection> HTMLFormElement::elementsForNativeBindings()
658 {
659     return elements();
660 }
661
662 String HTMLFormElement::name() const
663 {
664     return getNameAttribute();
665 }
666
667 bool HTMLFormElement::noValidate() const
668 {
669     return hasAttributeWithoutSynchronization(novalidateAttr);
670 }
671
672 // FIXME: This function should be removed because it does not do the same thing as the
673 // JavaScript binding for action, which treats action as a URL attribute. Last time I
674 // (Darin Adler) removed this, someone added it back, so I am leaving it in for now.
675 String HTMLFormElement::action() const
676 {
677     return attributeWithoutSynchronization(actionAttr);
678 }
679
680 void HTMLFormElement::setAction(const String &value)
681 {
682     setAttributeWithoutSynchronization(actionAttr, value);
683 }
684
685 void HTMLFormElement::setEnctype(const String &value)
686 {
687     setAttributeWithoutSynchronization(enctypeAttr, value);
688 }
689
690 String HTMLFormElement::method() const
691 {
692     return FormSubmission::Attributes::methodString(m_attributes.method());
693 }
694
695 void HTMLFormElement::setMethod(const String &value)
696 {
697     setAttributeWithoutSynchronization(methodAttr, value);
698 }
699
700 String HTMLFormElement::target() const
701 {
702     return attributeWithoutSynchronization(targetAttr);
703 }
704
705 bool HTMLFormElement::wasUserSubmitted() const
706 {
707     return m_wasUserSubmitted;
708 }
709
710 HTMLFormControlElement* HTMLFormElement::defaultButton() const
711 {
712     if (!m_defaultButton) {
713         for (auto& associatedElement : m_associatedElements) {
714             if (!is<HTMLFormControlElement>(*associatedElement))
715                 continue;
716             HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*associatedElement);
717             if (control.isSuccessfulSubmitButton()) {
718                 m_defaultButton = &control;
719                 break;
720             }
721         }
722     }
723     return m_defaultButton;
724 }
725
726 void HTMLFormElement::resetDefaultButton()
727 {
728     if (!m_defaultButton) {
729         // Computing the default button is not cheap, we don't want to do it unless needed.
730         // If there was no default button set, the only style to invalidate is the element
731         // being added to the form. This is done explicitely in registerFormElement().
732         return;
733     }
734
735     HTMLFormControlElement* oldDefault = m_defaultButton;
736     m_defaultButton = nullptr;
737     defaultButton();
738     if (m_defaultButton != oldDefault) {
739         if (oldDefault)
740             oldDefault->setNeedsStyleRecalc();
741         if (m_defaultButton)
742             m_defaultButton->setNeedsStyleRecalc();
743     }
744 }
745
746 bool HTMLFormElement::checkValidity()
747 {
748     Vector<RefPtr<HTMLFormControlElement>> controls;
749     return !checkInvalidControlsAndCollectUnhandled(controls);
750 }
751
752 bool HTMLFormElement::checkInvalidControlsAndCollectUnhandled(Vector<RefPtr<HTMLFormControlElement>>& unhandledInvalidControls)
753 {
754     Ref<HTMLFormElement> protectedThis(*this);
755     // Copy m_associatedElements because event handlers called from
756     // HTMLFormControlElement::checkValidity() might change m_associatedElements.
757     Vector<RefPtr<FormAssociatedElement>> elements;
758     elements.reserveCapacity(m_associatedElements.size());
759     for (auto& associatedElement : m_associatedElements)
760         elements.append(associatedElement);
761     bool hasInvalidControls = false;
762     for (auto& element : elements) {
763         if (element->form() == this && is<HTMLFormControlElement>(*element)) {
764             HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*element);
765             if (!control.checkValidity(&unhandledInvalidControls) && control.form() == this)
766                 hasInvalidControls = true;
767         }
768     }
769     return hasInvalidControls;
770 }
771
772 bool HTMLFormElement::reportValidity()
773 {
774     return validateInteractively();
775 }
776
777 #ifndef NDEBUG
778 void HTMLFormElement::assertItemCanBeInPastNamesMap(FormNamedItem* item) const
779 {
780     ASSERT_WITH_SECURITY_IMPLICATION(item);
781     HTMLElement& element = item->asHTMLElement();
782     ASSERT_WITH_SECURITY_IMPLICATION(element.form() == this);
783
784     if (item->isFormAssociatedElement()) {
785         ASSERT_WITH_SECURITY_IMPLICATION(m_associatedElements.find(static_cast<FormAssociatedElement*>(item)) != notFound);
786         return;
787     }
788
789     ASSERT_WITH_SECURITY_IMPLICATION(element.hasTagName(imgTag));
790     ASSERT_WITH_SECURITY_IMPLICATION(m_imageElements.find(&downcast<HTMLImageElement>(element)) != notFound);
791 }
792 #else
793 inline void HTMLFormElement::assertItemCanBeInPastNamesMap(FormNamedItem*) const
794 {
795 }
796 #endif
797
798 HTMLElement* HTMLFormElement::elementFromPastNamesMap(const AtomicString& pastName) const
799 {
800     if (pastName.isEmpty() || !m_pastNamesMap)
801         return nullptr;
802     FormNamedItem* item = m_pastNamesMap->get(pastName.impl());
803     if (!item)
804         return nullptr;
805     assertItemCanBeInPastNamesMap(item);
806     return &item->asHTMLElement();
807 }
808
809 void HTMLFormElement::addToPastNamesMap(FormNamedItem* item, const AtomicString& pastName)
810 {
811     assertItemCanBeInPastNamesMap(item);
812     if (pastName.isEmpty())
813         return;
814     if (!m_pastNamesMap)
815         m_pastNamesMap = std::make_unique<PastNamesMap>();
816     m_pastNamesMap->set(pastName.impl(), item);
817 }
818
819 void HTMLFormElement::removeFromPastNamesMap(FormNamedItem* item)
820 {
821     ASSERT(item);
822     if (!m_pastNamesMap)
823         return;
824
825     for (auto& pastName : m_pastNamesMap->values()) {
826         if (pastName == item)
827             pastName = nullptr; // Keep looping. Single element can have multiple names.
828     }
829 }
830
831 bool HTMLFormElement::matchesValidPseudoClass() const
832 {
833     return m_invalidAssociatedFormControls.isEmpty();
834 }
835
836 bool HTMLFormElement::matchesInvalidPseudoClass() const
837 {
838     return !m_invalidAssociatedFormControls.isEmpty();
839 }
840
841 // FIXME: Use Ref<HTMLElement> for the function result since there are no non-HTML elements returned here.
842 Vector<Ref<Element>> HTMLFormElement::namedElements(const AtomicString& name)
843 {
844     // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem
845     Vector<Ref<Element>> namedItems = elements()->namedItems(name);
846
847     HTMLElement* elementFromPast = elementFromPastNamesMap(name);
848     if (namedItems.size() == 1 && namedItems.first().ptr() != elementFromPast)
849         addToPastNamesMap(downcast<HTMLElement>(namedItems.first().get()).asFormNamedItem(), name);
850     else if (elementFromPast && namedItems.isEmpty())
851         namedItems.append(*elementFromPast);
852
853     return namedItems;
854 }
855
856 void HTMLFormElement::resumeFromDocumentSuspension()
857 {
858     ASSERT(!shouldAutocomplete());
859
860     for (auto& associatedElement : m_associatedElements) {
861         if (is<HTMLFormControlElement>(*associatedElement))
862             downcast<HTMLFormControlElement>(*associatedElement).reset();
863     }
864 }
865
866 void HTMLFormElement::didMoveToNewDocument(Document* oldDocument)
867 {
868     if (!shouldAutocomplete()) {
869         if (oldDocument)
870             oldDocument->unregisterForDocumentSuspensionCallbacks(this);
871         document().registerForDocumentSuspensionCallbacks(this);
872     }
873
874     HTMLElement::didMoveToNewDocument(oldDocument);
875 }
876
877 bool HTMLFormElement::shouldAutocomplete() const
878 {
879     return !equalLettersIgnoringASCIICase(attributeWithoutSynchronization(autocompleteAttr), "off");
880 }
881
882 void HTMLFormElement::finishParsingChildren()
883 {
884     HTMLElement::finishParsingChildren();
885     document().formController().restoreControlStateIn(*this);
886 }
887
888 void HTMLFormElement::copyNonAttributePropertiesFromElement(const Element& source)
889 {
890     m_wasDemoted = static_cast<const HTMLFormElement&>(source).m_wasDemoted;
891     HTMLElement::copyNonAttributePropertiesFromElement(source);
892 }
893
894 HTMLFormElement* HTMLFormElement::findClosestFormAncestor(const Element& startElement)
895 {
896     return const_cast<HTMLFormElement*>(ancestorsOfType<HTMLFormElement>(startElement).first());
897 }
898
899 void HTMLFormElement::setAutocomplete(const AtomicString& value)
900 {
901     setAttributeWithoutSynchronization(autocompleteAttr, value);
902 }
903
904 const AtomicString& HTMLFormElement::autocomplete() const
905 {
906     static NeverDestroyed<AtomicString> on("on", AtomicString::ConstructFromLiteral);
907     static NeverDestroyed<AtomicString> off("off", AtomicString::ConstructFromLiteral);
908
909     return equalIgnoringASCIICase(attributeWithoutSynchronization(autocompleteAttr), "off") ? off : on;
910 }
911
912 } // namespace