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