6aa0f5f2d36802661de8f837df7cf3da36fd211f
[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 (unsigned i = 0; i < m_associatedElements.size(); ++i)
90         m_associatedElements[i]->formWillBeDestroyed();
91     for (unsigned i = 0; i < m_imageElements.size(); ++i)
92         m_imageElements[i]->m_form = 0;
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 (unsigned i = 0; i < associatedElements.size(); ++i)
151         associatedElements[i]->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 (unsigned i = 0; i < m_associatedElements.size(); ++i)
169         if (m_associatedElements[i]->isEnumeratable())
170             ++len;
171     return len;
172 }
173
174 Node* HTMLFormElement::item(unsigned index)
175 {
176     return elements()->item(index);
177 }
178
179 void HTMLFormElement::submitImplicitly(Event* event, bool fromImplicitSubmissionTrigger)
180 {
181     unsigned submissionTriggerCount = 0;
182     for (unsigned i = 0; i < m_associatedElements.size(); ++i) {
183         FormAssociatedElement* formAssociatedElement = m_associatedElements[i];
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 (unsigned i = 0; i < m_associatedElements.size(); ++i) {
225         if (is<HTMLFormControlElement>(*m_associatedElements[i]))
226             downcast<HTMLFormControlElement>(*m_associatedElements[i]).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 (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) {
243         HTMLElement& element = unhandledInvalidControls[i]->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 (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) {
256             FormAssociatedElement& control = *unhandledInvalidControls[i];
257             HTMLElement& element = control.asHTMLElement();
258             if (element.inDocument() && element.isFocusable())
259                 continue;
260             String message("An invalid form control with name='%name' is not focusable.");
261             message.replace("%name", control.name());
262             document().addConsoleMessage(MessageSource::Rendering, MessageLevel::Error, message);
263         }
264     }
265
266     return false;
267 }
268
269 void HTMLFormElement::prepareForSubmission(Event* event)
270 {
271     Frame* frame = document().frame();
272     if (m_isSubmittingOrPreparingForSubmission || !frame)
273         return;
274
275     m_isSubmittingOrPreparingForSubmission = true;
276     m_shouldSubmit = false;
277
278     // Interactive validation must be done before dispatching the submit event.
279     if (!validateInteractively(event)) {
280         m_isSubmittingOrPreparingForSubmission = false;
281         return;
282     }
283
284     StringPairVector controlNamesAndValues;
285     getTextFieldValues(controlNamesAndValues);
286     RefPtr<FormState> formState = FormState::create(this, controlNamesAndValues, &document(), NotSubmittedByJavaScript);
287     frame->loader().client().dispatchWillSendSubmitEvent(formState.release());
288
289     Ref<HTMLFormElement> protect(*this);
290     // Event handling can result in m_shouldSubmit becoming true, regardless of dispatchEvent() return value.
291     if (dispatchEvent(Event::create(eventNames().submitEvent, true, true)))
292         m_shouldSubmit = true;
293
294     m_isSubmittingOrPreparingForSubmission = false;
295
296     if (m_shouldSubmit)
297         submit(event, true, true, NotSubmittedByJavaScript);
298 }
299
300 void HTMLFormElement::submit()
301 {
302     submit(0, false, true, NotSubmittedByJavaScript);
303 }
304
305 void HTMLFormElement::submitFromJavaScript()
306 {
307     submit(0, false, ScriptController::processingUserGesture(), SubmittedByJavaScript);
308 }
309
310 void HTMLFormElement::getTextFieldValues(StringPairVector& fieldNamesAndValues) const
311 {
312     ASSERT_ARG(fieldNamesAndValues, fieldNamesAndValues.isEmpty());
313
314     fieldNamesAndValues.reserveCapacity(m_associatedElements.size());
315     for (unsigned i = 0; i < m_associatedElements.size(); ++i) {
316         FormAssociatedElement& control = *m_associatedElements[i];
317         HTMLElement& element = control.asHTMLElement();
318         if (!is<HTMLInputElement>(element))
319             continue;
320         HTMLInputElement& input = downcast<HTMLInputElement>(element);
321         if (!input.isTextField())
322             continue;
323         fieldNamesAndValues.append(std::make_pair(input.name().string(), input.value()));
324     }
325 }
326
327 void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool processingUserGesture, FormSubmissionTrigger formSubmissionTrigger)
328 {
329     FrameView* view = document().view();
330     Frame* frame = document().frame();
331     if (!view || !frame)
332         return;
333
334     if (m_isSubmittingOrPreparingForSubmission) {
335         m_shouldSubmit = true;
336         return;
337     }
338
339     m_isSubmittingOrPreparingForSubmission = true;
340     m_wasUserSubmitted = processingUserGesture;
341
342     RefPtr<HTMLFormControlElement> firstSuccessfulSubmitButton;
343     bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button?
344
345     for (unsigned i = 0; i < m_associatedElements.size(); ++i) {
346         FormAssociatedElement* associatedElement = m_associatedElements[i];
347         if (!is<HTMLFormControlElement>(*associatedElement))
348             continue;
349         if (needButtonActivation) {
350             HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*associatedElement);
351             if (control.isActivatedSubmit())
352                 needButtonActivation = false;
353             else if (!firstSuccessfulSubmitButton && control.isSuccessfulSubmitButton())
354                 firstSuccessfulSubmitButton = &control;
355         }
356     }
357
358     if (needButtonActivation && firstSuccessfulSubmitButton)
359         firstSuccessfulSubmitButton->setActivatedSubmit(true);
360
361     LockHistory lockHistory = processingUserGesture ? LockHistory::No : LockHistory::Yes;
362     Ref<HTMLFormElement> protect(*this); // Form submission can execute arbitary JavaScript.
363     frame->loader().submitForm(FormSubmission::create(this, m_attributes, event, lockHistory, formSubmissionTrigger));
364
365     if (needButtonActivation && firstSuccessfulSubmitButton)
366         firstSuccessfulSubmitButton->setActivatedSubmit(false);
367
368     m_shouldSubmit = false;
369     m_isSubmittingOrPreparingForSubmission = false;
370 }
371
372 void HTMLFormElement::reset()
373 {
374     Frame* frame = document().frame();
375     if (m_isInResetFunction || !frame)
376         return;
377
378     m_isInResetFunction = true;
379
380     if (!dispatchEvent(Event::create(eventNames().resetEvent, true, true))) {
381         m_isInResetFunction = false;
382         return;
383     }
384
385     for (unsigned i = 0; i < m_associatedElements.size(); ++i) {
386         if (is<HTMLFormControlElement>(*m_associatedElements[i]))
387             downcast<HTMLFormControlElement>(*m_associatedElements[i]).reset();
388     }
389
390     m_isInResetFunction = false;
391 }
392
393 #if ENABLE(IOS_AUTOCORRECT_AND_AUTOCAPITALIZE)
394 // FIXME: We should look to share these methods with class HTMLFormControlElement instead of duplicating them.
395
396 bool HTMLFormElement::autocorrect() const
397 {
398     const AtomicString& autocorrectValue = fastGetAttribute(autocorrectAttr);
399     if (!autocorrectValue.isEmpty())
400         return !equalIgnoringCase(autocorrectValue, "off");
401     if (HTMLFormElement* form = this->form())
402         return form->autocorrect();
403     return true;
404 }
405
406 void HTMLFormElement::setAutocorrect(bool autocorrect)
407 {
408     setAttribute(autocorrectAttr, autocorrect ? AtomicString("on", AtomicString::ConstructFromLiteral) : AtomicString("off", AtomicString::ConstructFromLiteral));
409 }
410
411 WebAutocapitalizeType HTMLFormElement::autocapitalizeType() const
412 {
413     return autocapitalizeTypeForAttributeValue(fastGetAttribute(autocapitalizeAttr));
414 }
415
416 const AtomicString& HTMLFormElement::autocapitalize() const
417 {
418     return stringForAutocapitalizeType(autocapitalizeType());
419 }
420
421 void HTMLFormElement::setAutocapitalize(const AtomicString& value)
422 {
423     setAttribute(autocapitalizeAttr, value);
424 }
425
426 #endif
427
428 #if ENABLE(REQUEST_AUTOCOMPLETE)
429
430 void HTMLFormElement::requestAutocomplete()
431 {
432     Frame* frame = document().frame();
433     if (!frame)
434         return;
435
436     if (!shouldAutocomplete() || !ScriptController::processingUserGesture()) {
437         finishRequestAutocomplete(AutocompleteResult::ErrorDisabled);
438         return;
439     }
440
441     StringPairVector controlNamesAndValues;
442     getTextFieldValues(controlNamesAndValues);
443
444     RefPtr<FormState> formState = FormState::create(this, controlNamesAndValues, &document(), SubmittedByJavaScript);
445     frame->loader().client().didRequestAutocomplete(formState.release());
446 }
447
448 void HTMLFormElement::finishRequestAutocomplete(AutocompleteResult result)
449 {
450     RefPtr<Event> event;
451     switch (result) {
452     case AutocompleteResult::Success:
453         event = Event::create(eventNames().autocompleteEvent, false, false);
454         break;
455     case AutocompleteResult::ErrorDisabled:
456         event = AutocompleteErrorEvent::create("disabled");
457         break;
458     case AutocompleteResult::ErrorCancel:
459         event = AutocompleteErrorEvent::create("cancel");
460         break;
461     case AutocompleteResult::ErrorInvalid:
462         event = AutocompleteErrorEvent::create("invalid");
463         break;
464     }
465
466     event->setTarget(this);
467     m_pendingAutocompleteEvents.append(event.release());
468
469     // Dispatch events later as this API is meant to work asynchronously in all situations and implementations.
470     if (!m_requestAutocompleteTimer.isActive())
471         m_requestAutocompleteTimer.startOneShot(0);
472 }
473
474 void HTMLFormElement::requestAutocompleteTimerFired()
475 {
476     Vector<RefPtr<Event>> pendingEvents;
477     m_pendingAutocompleteEvents.swap(pendingEvents);
478     for (auto& pendingEvent : pendingEvents)
479         dispatchEvent(pendingEvent.release());
480 }
481
482 #endif
483
484 void HTMLFormElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
485 {
486     if (name == actionAttr)
487         m_attributes.parseAction(value);
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().registerForPageCacheSuspensionCallbacks(this);
499         else
500             document().unregisterForPageCacheSuspensionCallbacks(this);
501     }
502     else
503         HTMLElement::parseAttribute(name, value);
504 }
505
506 unsigned HTMLFormElement::formElementIndexWithFormAttribute(Element* element, unsigned rangeStart, unsigned rangeEnd)
507 {
508     if (m_associatedElements.isEmpty())
509         return 0;
510
511     ASSERT(rangeStart <= rangeEnd);
512
513     if (rangeStart == rangeEnd)
514         return rangeStart;
515
516     unsigned left = rangeStart;
517     unsigned right = rangeEnd - 1;
518     unsigned short position;
519
520     // Does binary search on m_associatedElements in order to find the index
521     // to be inserted.
522     while (left != right) {
523         unsigned middle = left + ((right - left) / 2);
524         ASSERT(middle < m_associatedElementsBeforeIndex || middle >= m_associatedElementsAfterIndex);
525         position = element->compareDocumentPosition(&m_associatedElements[middle]->asHTMLElement());
526         if (position & DOCUMENT_POSITION_FOLLOWING)
527             right = middle;
528         else
529             left = middle + 1;
530     }
531     
532     ASSERT(left < m_associatedElementsBeforeIndex || left >= m_associatedElementsAfterIndex);
533     position = element->compareDocumentPosition(&m_associatedElements[left]->asHTMLElement());
534     if (position & DOCUMENT_POSITION_FOLLOWING)
535         return left;
536     return left + 1;
537 }
538
539 unsigned HTMLFormElement::formElementIndex(FormAssociatedElement* associatedElement)
540 {
541     ASSERT(associatedElement);
542
543     HTMLElement& associatedHTMLElement = associatedElement->asHTMLElement();
544
545     // Treats separately the case where this element has the form attribute
546     // for performance consideration.
547     if (associatedHTMLElement.fastHasAttribute(formAttr)) {
548         unsigned short position = compareDocumentPosition(&associatedHTMLElement);
549         if (position & DOCUMENT_POSITION_PRECEDING) {
550             ++m_associatedElementsBeforeIndex;
551             ++m_associatedElementsAfterIndex;
552             return HTMLFormElement::formElementIndexWithFormAttribute(&associatedHTMLElement, 0, m_associatedElementsBeforeIndex - 1);
553         }
554         if (position & DOCUMENT_POSITION_FOLLOWING && !(position & DOCUMENT_POSITION_CONTAINED_BY))
555             return HTMLFormElement::formElementIndexWithFormAttribute(&associatedHTMLElement, m_associatedElementsAfterIndex, m_associatedElements.size());
556     }
557
558     unsigned currentAssociatedElementsAfterIndex = m_associatedElementsAfterIndex;
559     ++m_associatedElementsAfterIndex;
560
561     if (!associatedHTMLElement.isDescendantOf(this))
562         return currentAssociatedElementsAfterIndex;
563
564     // Check for the special case where this element is the very last thing in
565     // the form's tree of children; we don't want to walk the entire tree in that
566     // common case that occurs during parsing; instead we'll just return a value
567     // that says "add this form element to the end of the array".
568     auto descendants = descendantsOfType<HTMLElement>(*this);
569     auto it = descendants.beginAt(associatedHTMLElement);
570     auto end = descendants.end();
571     if (++it == end)
572         return currentAssociatedElementsAfterIndex;
573
574     unsigned i = m_associatedElementsBeforeIndex;
575     for (auto& element : descendants) {
576         if (&element == &associatedHTMLElement)
577             return i;
578         if (!is<HTMLFormControlElement>(element) && !is<HTMLObjectElement>(element))
579             continue;
580         if (element.form() != this)
581             continue;
582         ++i;
583     }
584     return currentAssociatedElementsAfterIndex;
585 }
586
587 void HTMLFormElement::registerFormElement(FormAssociatedElement* e)
588 {
589     m_associatedElements.insert(formElementIndex(e), e);
590 }
591
592 void HTMLFormElement::removeFormElement(FormAssociatedElement* e)
593 {
594     unsigned index = m_associatedElements.find(e);
595     ASSERT_WITH_SECURITY_IMPLICATION(index < m_associatedElements.size());
596     if (index < m_associatedElementsBeforeIndex)
597         --m_associatedElementsBeforeIndex;
598     if (index < m_associatedElementsAfterIndex)
599         --m_associatedElementsAfterIndex;
600     removeFromPastNamesMap(e);
601     m_associatedElements.remove(index);
602 }
603
604 void HTMLFormElement::registerInvalidAssociatedFormControl(const HTMLFormControlElement& formControlElement)
605 {
606     ASSERT_WITH_MESSAGE(!is<HTMLFieldSetElement>(formControlElement), "FieldSet are never candidates for constraint validation.");
607     ASSERT(static_cast<const Element&>(formControlElement).matchesInvalidPseudoClass());
608
609     if (m_invalidAssociatedFormControls.isEmpty())
610         setNeedsStyleRecalc();
611     m_invalidAssociatedFormControls.add(&formControlElement);
612 }
613
614 void HTMLFormElement::removeInvalidAssociatedFormControlIfNeeded(const HTMLFormControlElement& formControlElement)
615 {
616     if (m_invalidAssociatedFormControls.remove(&formControlElement)) {
617         if (m_invalidAssociatedFormControls.isEmpty())
618             setNeedsStyleRecalc();
619     }
620 }
621
622 bool HTMLFormElement::isURLAttribute(const Attribute& attribute) const
623 {
624     return attribute.name() == actionAttr || HTMLElement::isURLAttribute(attribute);
625 }
626
627 void HTMLFormElement::registerImgElement(HTMLImageElement* e)
628 {
629     ASSERT(m_imageElements.find(e) == notFound);
630     m_imageElements.append(e);
631 }
632
633 void HTMLFormElement::removeImgElement(HTMLImageElement* e)
634 {
635     removeFromPastNamesMap(e);
636     bool removed = m_imageElements.removeFirst(e);
637     ASSERT_UNUSED(removed, removed);
638 }
639
640 Ref<HTMLCollection> HTMLFormElement::elements()
641 {
642     return ensureRareData().ensureNodeLists().addCachedCollection<HTMLFormControlsCollection>(*this, FormControls);
643 }
644
645 String HTMLFormElement::name() const
646 {
647     return getNameAttribute();
648 }
649
650 bool HTMLFormElement::noValidate() const
651 {
652     return fastHasAttribute(novalidateAttr);
653 }
654
655 // FIXME: This function should be removed because it does not do the same thing as the
656 // JavaScript binding for action, which treats action as a URL attribute. Last time I
657 // (Darin Adler) removed this, someone added it back, so I am leaving it in for now.
658 String HTMLFormElement::action() const
659 {
660     return fastGetAttribute(actionAttr);
661 }
662
663 void HTMLFormElement::setAction(const String &value)
664 {
665     setAttribute(actionAttr, value);
666 }
667
668 void HTMLFormElement::setEnctype(const String &value)
669 {
670     setAttribute(enctypeAttr, value);
671 }
672
673 String HTMLFormElement::method() const
674 {
675     return FormSubmission::Attributes::methodString(m_attributes.method());
676 }
677
678 void HTMLFormElement::setMethod(const String &value)
679 {
680     setAttribute(methodAttr, value);
681 }
682
683 String HTMLFormElement::target() const
684 {
685     return getAttribute(targetAttr);
686 }
687
688 bool HTMLFormElement::wasUserSubmitted() const
689 {
690     return m_wasUserSubmitted;
691 }
692
693 HTMLFormControlElement* HTMLFormElement::defaultButton() const
694 {
695     for (unsigned i = 0; i < m_associatedElements.size(); ++i) {
696         if (!is<HTMLFormControlElement>(*m_associatedElements[i]))
697             continue;
698         HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*m_associatedElements[i]);
699         if (control.isSuccessfulSubmitButton())
700             return &control;
701     }
702
703     return nullptr;
704 }
705
706 bool HTMLFormElement::checkValidity()
707 {
708     Vector<RefPtr<FormAssociatedElement>> controls;
709     return !checkInvalidControlsAndCollectUnhandled(controls);
710 }
711
712 bool HTMLFormElement::checkInvalidControlsAndCollectUnhandled(Vector<RefPtr<FormAssociatedElement>>& unhandledInvalidControls)
713 {
714     Ref<HTMLFormElement> protect(*this);
715     // Copy m_associatedElements because event handlers called from
716     // HTMLFormControlElement::checkValidity() might change m_associatedElements.
717     Vector<RefPtr<FormAssociatedElement>> elements;
718     elements.reserveCapacity(m_associatedElements.size());
719     for (unsigned i = 0; i < m_associatedElements.size(); ++i)
720         elements.append(m_associatedElements[i]);
721     bool hasInvalidControls = false;
722     for (unsigned i = 0; i < elements.size(); ++i) {
723         if (elements[i]->form() == this && is<HTMLFormControlElement>(*elements[i])) {
724             HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*elements[i]);
725             if (!control.checkValidity(&unhandledInvalidControls) && control.form() == this)
726                 hasInvalidControls = true;
727         }
728     }
729     return hasInvalidControls;
730 }
731
732 #ifndef NDEBUG
733 void HTMLFormElement::assertItemCanBeInPastNamesMap(FormNamedItem* item) const
734 {
735     ASSERT_WITH_SECURITY_IMPLICATION(item);
736     HTMLElement& element = item->asHTMLElement();
737     ASSERT_WITH_SECURITY_IMPLICATION(element.form() == this);
738
739     if (item->isFormAssociatedElement()) {
740         ASSERT_WITH_SECURITY_IMPLICATION(m_associatedElements.find(static_cast<FormAssociatedElement*>(item)) != notFound);
741         return;
742     }
743
744     ASSERT_WITH_SECURITY_IMPLICATION(element.hasTagName(imgTag));
745     ASSERT_WITH_SECURITY_IMPLICATION(m_imageElements.find(&downcast<HTMLImageElement>(element)) != notFound);
746 }
747 #else
748 inline void HTMLFormElement::assertItemCanBeInPastNamesMap(FormNamedItem*) const
749 {
750 }
751 #endif
752
753 HTMLElement* HTMLFormElement::elementFromPastNamesMap(const AtomicString& pastName) const
754 {
755     if (pastName.isEmpty() || !m_pastNamesMap)
756         return nullptr;
757     FormNamedItem* item = m_pastNamesMap->get(pastName.impl());
758     if (!item)
759         return nullptr;
760     assertItemCanBeInPastNamesMap(item);
761     return &item->asHTMLElement();
762 }
763
764 void HTMLFormElement::addToPastNamesMap(FormNamedItem* item, const AtomicString& pastName)
765 {
766     assertItemCanBeInPastNamesMap(item);
767     if (pastName.isEmpty())
768         return;
769     if (!m_pastNamesMap)
770         m_pastNamesMap = std::make_unique<PastNamesMap>();
771     m_pastNamesMap->set(pastName.impl(), item);
772 }
773
774 void HTMLFormElement::removeFromPastNamesMap(FormNamedItem* item)
775 {
776     ASSERT(item);
777     if (!m_pastNamesMap)
778         return;
779
780     PastNamesMap::iterator end = m_pastNamesMap->end();
781     for (PastNamesMap::iterator it = m_pastNamesMap->begin(); it != end; ++it) {
782         if (it->value == item)
783             it->value = 0; // Keep looping. Single element can have multiple names.
784     }
785 }
786
787 bool HTMLFormElement::matchesValidPseudoClass() const
788 {
789     return m_invalidAssociatedFormControls.isEmpty();
790 }
791
792 bool HTMLFormElement::matchesInvalidPseudoClass() const
793 {
794     return !m_invalidAssociatedFormControls.isEmpty();
795 }
796
797 // FIXME: Use Ref<HTMLElement> for the function result since there are no non-HTML elements returned here.
798 Vector<Ref<Element>> HTMLFormElement::namedElements(const AtomicString& name)
799 {
800     // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem
801     Vector<Ref<Element>> namedItems = elements()->namedItems(name);
802
803     HTMLElement* elementFromPast = elementFromPastNamesMap(name);
804     if (namedItems.size() == 1 && namedItems.first().ptr() != elementFromPast)
805         addToPastNamesMap(downcast<HTMLElement>(namedItems.first().get()).asFormNamedItem(), name);
806     else if (elementFromPast && namedItems.isEmpty())
807         namedItems.append(*elementFromPast);
808
809     return namedItems;
810 }
811
812 void HTMLFormElement::documentDidResumeFromPageCache()
813 {
814     ASSERT(!shouldAutocomplete());
815
816     for (unsigned i = 0; i < m_associatedElements.size(); ++i) {
817         if (is<HTMLFormControlElement>(*m_associatedElements[i]))
818             downcast<HTMLFormControlElement>(*m_associatedElements[i]).reset();
819     }
820 }
821
822 void HTMLFormElement::didMoveToNewDocument(Document* oldDocument)
823 {
824     if (!shouldAutocomplete()) {
825         if (oldDocument)
826             oldDocument->unregisterForPageCacheSuspensionCallbacks(this);
827         document().registerForPageCacheSuspensionCallbacks(this);
828     }
829
830     HTMLElement::didMoveToNewDocument(oldDocument);
831 }
832
833 bool HTMLFormElement::shouldAutocomplete() const
834 {
835     return !equalIgnoringCase(fastGetAttribute(autocompleteAttr), "off");
836 }
837
838 void HTMLFormElement::finishParsingChildren()
839 {
840     HTMLElement::finishParsingChildren();
841     document().formController().restoreControlStateIn(*this);
842 }
843
844 void HTMLFormElement::copyNonAttributePropertiesFromElement(const Element& source)
845 {
846     m_wasDemoted = static_cast<const HTMLFormElement&>(source).m_wasDemoted;
847     HTMLElement::copyNonAttributePropertiesFromElement(source);
848 }
849
850 HTMLFormElement* HTMLFormElement::findClosestFormAncestor(const Element& startElement)
851 {
852     return const_cast<HTMLFormElement*>(ancestorsOfType<HTMLFormElement>(startElement).first());
853 }
854
855 } // namespace