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