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