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)
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.
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.
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.
26 #include "HTMLFormElement.h"
28 #include "AutocompleteErrorEvent.h"
29 #include "DOMFormData.h"
30 #include "DOMWindow.h"
32 #include "ElementIterator.h"
34 #include "EventNames.h"
35 #include "FormController.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"
47 #include "RenderTextControl.h"
48 #include "ScriptController.h"
55 using namespace HTMLNames;
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)
66 #if ENABLE(REQUEST_AUTOCOMPLETE)
67 , m_requestAutocompletetimer(*this, &HTMLFormElement::requestAutocompleteTimerFired)
70 ASSERT(hasTagName(formTag));
73 Ref<HTMLFormElement> HTMLFormElement::create(Document& document)
75 return adoptRef(*new HTMLFormElement(formTag, document));
78 Ref<HTMLFormElement> HTMLFormElement::create(const QualifiedName& tagName, Document& document)
80 return adoptRef(*new HTMLFormElement(tagName, document));
83 HTMLFormElement::~HTMLFormElement()
85 document().formController().willDeleteForm(this);
86 if (!shouldAutocomplete())
87 document().unregisterForPageCacheSuspensionCallbacks(this);
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;
95 bool HTMLFormElement::formWouldHaveSecureSubmission(const String& url)
97 return document().completeURL(url).protocolIs("https");
100 bool HTMLFormElement::rendererIsNeeded(const RenderStyle& style)
103 return HTMLElement::rendererIsNeeded(style);
105 auto parent = parentNode();
106 auto parentRenderer = parent->renderer();
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));
118 if (!parentIsTableElementPart)
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;
127 return formIsTablePart;
130 Node::InsertionNotificationRequest HTMLFormElement::insertedInto(ContainerNode& insertionPoint)
132 HTMLElement::insertedInto(insertionPoint);
133 if (insertionPoint.inDocument())
134 document().didAssociateFormControl(this);
135 return InsertionDone;
138 static inline Node* findRoot(Node* n)
141 for (; n; n = n->parentNode())
146 void HTMLFormElement::removedFrom(ContainerNode& insertionPoint)
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);
155 void HTMLFormElement::handleLocalEvents(Event& event)
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();
162 HTMLElement::handleLocalEvents(event);
165 unsigned HTMLFormElement::length() const
168 for (unsigned i = 0; i < m_associatedElements.size(); ++i)
169 if (m_associatedElements[i]->isEnumeratable())
174 Node* HTMLFormElement::item(unsigned index)
176 return elements()->item(index);
179 void HTMLFormElement::submitImplicitly(Event* event, bool fromImplicitSubmissionTrigger)
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))
186 HTMLFormControlElement& formElement = downcast<HTMLFormControlElement>(*formAssociatedElement);
187 if (formElement.isSuccessfulSubmitButton()) {
188 if (formElement.renderer()) {
189 formElement.dispatchSimulatedClick(event);
192 } else if (formElement.canTriggerImplicitSubmission())
193 ++submissionTriggerCount;
196 if (!submissionTriggerCount)
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);
205 static inline HTMLFormControlElement* submitElementFromEvent(const Event* event)
207 for (Node* node = event->target()->toNode(); node; node = node->parentNode()) {
208 if (is<HTMLFormControlElement>(*node))
209 return downcast<HTMLFormControlElement>(node);
214 bool HTMLFormElement::validateInteractively(Event* event)
217 if (!document().page() || !document().page()->settings().interactiveFormValidationEnabled() || noValidate())
220 HTMLFormControlElement* submitElement = submitElementFromEvent(event);
221 if (submitElement && submitElement->formNoValidate())
224 for (unsigned i = 0; i < m_associatedElements.size(); ++i) {
225 if (is<HTMLFormControlElement>(*m_associatedElements[i]))
226 downcast<HTMLFormControlElement>(*m_associatedElements[i]).hideVisibleValidationMessage();
229 Vector<RefPtr<FormAssociatedElement>> unhandledInvalidControls;
230 if (!checkInvalidControlsAndCollectUnhandled(unhandledInvalidControls))
232 // Because the form has invalid controls, we abort the form submission and
233 // show a validation message on a focusable form control.
235 // Needs to update layout now because we'd like to call isFocusable(), which
236 // has !renderer()->needsLayout() assertion.
237 document().updateLayoutIgnorePendingStylesheets();
239 Ref<HTMLFormElement> protect(*this);
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);
247 if (is<HTMLFormControlElement>(element))
248 downcast<HTMLFormControlElement>(element).updateVisibleValidationMessage();
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())
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);
269 void HTMLFormElement::prepareForSubmission(Event* event)
271 Frame* frame = document().frame();
272 if (m_isSubmittingOrPreparingForSubmission || !frame)
275 m_isSubmittingOrPreparingForSubmission = true;
276 m_shouldSubmit = false;
278 // Interactive validation must be done before dispatching the submit event.
279 if (!validateInteractively(event)) {
280 m_isSubmittingOrPreparingForSubmission = false;
284 StringPairVector controlNamesAndValues;
285 getTextFieldValues(controlNamesAndValues);
286 RefPtr<FormState> formState = FormState::create(this, controlNamesAndValues, &document(), NotSubmittedByJavaScript);
287 frame->loader().client().dispatchWillSendSubmitEvent(formState.release());
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;
294 m_isSubmittingOrPreparingForSubmission = false;
297 submit(event, true, true, NotSubmittedByJavaScript);
300 void HTMLFormElement::submit()
302 submit(0, false, true, NotSubmittedByJavaScript);
305 void HTMLFormElement::submitFromJavaScript()
307 submit(0, false, ScriptController::processingUserGesture(), SubmittedByJavaScript);
310 void HTMLFormElement::getTextFieldValues(StringPairVector& fieldNamesAndValues) const
312 ASSERT_ARG(fieldNamesAndValues, fieldNamesAndValues.isEmpty());
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))
320 HTMLInputElement& input = downcast<HTMLInputElement>(element);
321 if (!input.isTextField())
323 fieldNamesAndValues.append(std::make_pair(input.name().string(), input.value()));
327 void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool processingUserGesture, FormSubmissionTrigger formSubmissionTrigger)
329 FrameView* view = document().view();
330 Frame* frame = document().frame();
334 if (m_isSubmittingOrPreparingForSubmission) {
335 m_shouldSubmit = true;
339 m_isSubmittingOrPreparingForSubmission = true;
340 m_wasUserSubmitted = processingUserGesture;
342 RefPtr<HTMLFormControlElement> firstSuccessfulSubmitButton;
343 bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button?
345 for (unsigned i = 0; i < m_associatedElements.size(); ++i) {
346 FormAssociatedElement* associatedElement = m_associatedElements[i];
347 if (!is<HTMLFormControlElement>(*associatedElement))
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;
358 if (needButtonActivation && firstSuccessfulSubmitButton)
359 firstSuccessfulSubmitButton->setActivatedSubmit(true);
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));
365 if (needButtonActivation && firstSuccessfulSubmitButton)
366 firstSuccessfulSubmitButton->setActivatedSubmit(false);
368 m_shouldSubmit = false;
369 m_isSubmittingOrPreparingForSubmission = false;
372 void HTMLFormElement::reset()
374 Frame* frame = document().frame();
375 if (m_isInResetFunction || !frame)
378 m_isInResetFunction = true;
380 if (!dispatchEvent(Event::create(eventNames().resetEvent, true, true))) {
381 m_isInResetFunction = false;
385 for (unsigned i = 0; i < m_associatedElements.size(); ++i) {
386 if (is<HTMLFormControlElement>(*m_associatedElements[i]))
387 downcast<HTMLFormControlElement>(*m_associatedElements[i]).reset();
390 m_isInResetFunction = false;
393 #if ENABLE(IOS_AUTOCORRECT_AND_AUTOCAPITALIZE)
394 // FIXME: We should look to share these methods with class HTMLFormControlElement instead of duplicating them.
396 bool HTMLFormElement::autocorrect() const
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();
406 void HTMLFormElement::setAutocorrect(bool autocorrect)
408 setAttribute(autocorrectAttr, autocorrect ? AtomicString("on", AtomicString::ConstructFromLiteral) : AtomicString("off", AtomicString::ConstructFromLiteral));
411 WebAutocapitalizeType HTMLFormElement::autocapitalizeType() const
413 return autocapitalizeTypeForAttributeValue(fastGetAttribute(autocapitalizeAttr));
416 const AtomicString& HTMLFormElement::autocapitalize() const
418 return stringForAutocapitalizeType(autocapitalizeType());
421 void HTMLFormElement::setAutocapitalize(const AtomicString& value)
423 setAttribute(autocapitalizeAttr, value);
428 #if ENABLE(REQUEST_AUTOCOMPLETE)
430 void HTMLFormElement::requestAutocomplete()
432 Frame* frame = document().frame();
436 if (!shouldAutocomplete() || !ScriptController::processingUserGesture()) {
437 finishRequestAutocomplete(AutocompleteResult::ErrorDisabled);
441 StringPairVector controlNamesAndValues;
442 getTextFieldValues(controlNamesAndValues);
444 RefPtr<FormState> formState = FormState::create(this, controlNamesAndValues, &document(), SubmittedByJavaScript);
445 frame->loader().client().didRequestAutocomplete(formState.release());
448 void HTMLFormElement::finishRequestAutocomplete(AutocompleteResult result)
452 case AutocompleteResult::Success:
453 event = Event::create(eventNames().autocompleteEvent, false, false);
455 case AutocompleteResult::ErrorDisabled:
456 event = AutocompleteErrorEvent::create("disabled");
458 case AutocompleteResult::ErrorCancel:
459 event = AutocompleteErrorEvent::create("cancel");
461 case AutocompleteResult::ErrorInvalid:
462 event = AutocompleteErrorEvent::create("invalid");
466 event->setTarget(this);
467 m_pendingAutocompleteEvents.append(event.release());
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);
474 void HTMLFormElement::requestAutocompleteTimerFired()
476 Vector<RefPtr<Event>> pendingEvents;
477 m_pendingAutocompleteEvents.swap(pendingEvents);
478 for (auto& pendingEvent : pendingEvents)
479 dispatchEvent(pendingEvent.release());
484 void HTMLFormElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
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);
500 document().unregisterForPageCacheSuspensionCallbacks(this);
503 HTMLElement::parseAttribute(name, value);
506 unsigned HTMLFormElement::formElementIndexWithFormAttribute(Element* element, unsigned rangeStart, unsigned rangeEnd)
508 if (m_associatedElements.isEmpty())
511 ASSERT(rangeStart <= rangeEnd);
513 if (rangeStart == rangeEnd)
516 unsigned left = rangeStart;
517 unsigned right = rangeEnd - 1;
518 unsigned short position;
520 // Does binary search on m_associatedElements in order to find the index
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)
532 ASSERT(left < m_associatedElementsBeforeIndex || left >= m_associatedElementsAfterIndex);
533 position = element->compareDocumentPosition(&m_associatedElements[left]->asHTMLElement());
534 if (position & DOCUMENT_POSITION_FOLLOWING)
539 unsigned HTMLFormElement::formElementIndex(FormAssociatedElement* associatedElement)
541 ASSERT(associatedElement);
543 HTMLElement& associatedHTMLElement = associatedElement->asHTMLElement();
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);
554 if (position & DOCUMENT_POSITION_FOLLOWING && !(position & DOCUMENT_POSITION_CONTAINED_BY))
555 return HTMLFormElement::formElementIndexWithFormAttribute(&associatedHTMLElement, m_associatedElementsAfterIndex, m_associatedElements.size());
558 unsigned currentAssociatedElementsAfterIndex = m_associatedElementsAfterIndex;
559 ++m_associatedElementsAfterIndex;
561 if (!associatedHTMLElement.isDescendantOf(this))
562 return currentAssociatedElementsAfterIndex;
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();
572 return currentAssociatedElementsAfterIndex;
574 unsigned i = m_associatedElementsBeforeIndex;
575 for (auto& element : descendants) {
576 if (&element == &associatedHTMLElement)
578 if (!is<HTMLFormControlElement>(element) && !is<HTMLObjectElement>(element))
580 if (element.form() != this)
584 return currentAssociatedElementsAfterIndex;
587 void HTMLFormElement::registerFormElement(FormAssociatedElement* e)
589 m_associatedElements.insert(formElementIndex(e), e);
592 void HTMLFormElement::removeFormElement(FormAssociatedElement* e)
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);
604 void HTMLFormElement::registerInvalidAssociatedFormControl(const HTMLFormControlElement& formControlElement)
606 ASSERT_WITH_MESSAGE(!is<HTMLFieldSetElement>(formControlElement), "FieldSet are never candidates for constraint validation.");
607 ASSERT(static_cast<const Element&>(formControlElement).matchesInvalidPseudoClass());
609 if (m_invalidAssociatedFormControls.isEmpty())
610 setNeedsStyleRecalc();
611 m_invalidAssociatedFormControls.add(&formControlElement);
614 void HTMLFormElement::removeInvalidAssociatedFormControlIfNeeded(const HTMLFormControlElement& formControlElement)
616 if (m_invalidAssociatedFormControls.remove(&formControlElement)) {
617 if (m_invalidAssociatedFormControls.isEmpty())
618 setNeedsStyleRecalc();
622 bool HTMLFormElement::isURLAttribute(const Attribute& attribute) const
624 return attribute.name() == actionAttr || HTMLElement::isURLAttribute(attribute);
627 void HTMLFormElement::registerImgElement(HTMLImageElement* e)
629 ASSERT(m_imageElements.find(e) == notFound);
630 m_imageElements.append(e);
633 void HTMLFormElement::removeImgElement(HTMLImageElement* e)
635 removeFromPastNamesMap(e);
636 bool removed = m_imageElements.removeFirst(e);
637 ASSERT_UNUSED(removed, removed);
640 Ref<HTMLCollection> HTMLFormElement::elements()
642 return ensureRareData().ensureNodeLists().addCachedCollection<HTMLFormControlsCollection>(*this, FormControls);
645 String HTMLFormElement::name() const
647 return getNameAttribute();
650 bool HTMLFormElement::noValidate() const
652 return fastHasAttribute(novalidateAttr);
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
660 return fastGetAttribute(actionAttr);
663 void HTMLFormElement::setAction(const String &value)
665 setAttribute(actionAttr, value);
668 void HTMLFormElement::setEnctype(const String &value)
670 setAttribute(enctypeAttr, value);
673 String HTMLFormElement::method() const
675 return FormSubmission::Attributes::methodString(m_attributes.method());
678 void HTMLFormElement::setMethod(const String &value)
680 setAttribute(methodAttr, value);
683 String HTMLFormElement::target() const
685 return getAttribute(targetAttr);
688 bool HTMLFormElement::wasUserSubmitted() const
690 return m_wasUserSubmitted;
693 HTMLFormControlElement* HTMLFormElement::defaultButton() const
695 for (unsigned i = 0; i < m_associatedElements.size(); ++i) {
696 if (!is<HTMLFormControlElement>(*m_associatedElements[i]))
698 HTMLFormControlElement& control = downcast<HTMLFormControlElement>(*m_associatedElements[i]);
699 if (control.isSuccessfulSubmitButton())
706 bool HTMLFormElement::checkValidity()
708 Vector<RefPtr<FormAssociatedElement>> controls;
709 return !checkInvalidControlsAndCollectUnhandled(controls);
712 bool HTMLFormElement::checkInvalidControlsAndCollectUnhandled(Vector<RefPtr<FormAssociatedElement>>& unhandledInvalidControls)
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;
729 return hasInvalidControls;
733 void HTMLFormElement::assertItemCanBeInPastNamesMap(FormNamedItem* item) const
735 ASSERT_WITH_SECURITY_IMPLICATION(item);
736 HTMLElement& element = item->asHTMLElement();
737 ASSERT_WITH_SECURITY_IMPLICATION(element.form() == this);
739 if (item->isFormAssociatedElement()) {
740 ASSERT_WITH_SECURITY_IMPLICATION(m_associatedElements.find(static_cast<FormAssociatedElement*>(item)) != notFound);
744 ASSERT_WITH_SECURITY_IMPLICATION(element.hasTagName(imgTag));
745 ASSERT_WITH_SECURITY_IMPLICATION(m_imageElements.find(&downcast<HTMLImageElement>(element)) != notFound);
748 inline void HTMLFormElement::assertItemCanBeInPastNamesMap(FormNamedItem*) const
753 HTMLElement* HTMLFormElement::elementFromPastNamesMap(const AtomicString& pastName) const
755 if (pastName.isEmpty() || !m_pastNamesMap)
757 FormNamedItem* item = m_pastNamesMap->get(pastName.impl());
760 assertItemCanBeInPastNamesMap(item);
761 return &item->asHTMLElement();
764 void HTMLFormElement::addToPastNamesMap(FormNamedItem* item, const AtomicString& pastName)
766 assertItemCanBeInPastNamesMap(item);
767 if (pastName.isEmpty())
770 m_pastNamesMap = std::make_unique<PastNamesMap>();
771 m_pastNamesMap->set(pastName.impl(), item);
774 void HTMLFormElement::removeFromPastNamesMap(FormNamedItem* item)
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.
787 bool HTMLFormElement::matchesValidPseudoClass() const
789 return m_invalidAssociatedFormControls.isEmpty();
792 bool HTMLFormElement::matchesInvalidPseudoClass() const
794 return !m_invalidAssociatedFormControls.isEmpty();
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)
800 // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem
801 Vector<Ref<Element>> namedItems = elements()->namedItems(name);
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);
812 void HTMLFormElement::documentDidResumeFromPageCache()
814 ASSERT(!shouldAutocomplete());
816 for (unsigned i = 0; i < m_associatedElements.size(); ++i) {
817 if (is<HTMLFormControlElement>(*m_associatedElements[i]))
818 downcast<HTMLFormControlElement>(*m_associatedElements[i]).reset();
822 void HTMLFormElement::didMoveToNewDocument(Document* oldDocument)
824 if (!shouldAutocomplete()) {
826 oldDocument->unregisterForPageCacheSuspensionCallbacks(this);
827 document().registerForPageCacheSuspensionCallbacks(this);
830 HTMLElement::didMoveToNewDocument(oldDocument);
833 bool HTMLFormElement::shouldAutocomplete() const
835 return !equalIgnoringCase(fastGetAttribute(autocompleteAttr), "off");
838 void HTMLFormElement::finishParsingChildren()
840 HTMLElement::finishParsingChildren();
841 document().formController().restoreControlStateIn(*this);
844 void HTMLFormElement::copyNonAttributePropertiesFromElement(const Element& source)
846 m_wasDemoted = static_cast<const HTMLFormElement&>(source).m_wasDemoted;
847 HTMLElement::copyNonAttributePropertiesFromElement(source);
850 HTMLFormElement* HTMLFormElement::findClosestFormAncestor(const Element& startElement)
852 return const_cast<HTMLFormElement*>(ancestorsOfType<HTMLFormElement>(startElement).first());