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 "core/html/HTMLFormElement.h"
27
28#include "bindings/core/v8/ScriptController.h"
29#include "bindings/core/v8/ScriptEventListener.h"
30#include "bindings/core/v8/V8DOMActivityLogger.h"
31#include "core/HTMLNames.h"
32#include "core/dom/Attribute.h"
33#include "core/dom/Document.h"
34#include "core/dom/ElementTraversal.h"
35#include "core/dom/IdTargetObserverRegistry.h"
36#include "core/dom/NodeListsNodeData.h"
37#include "core/events/AutocompleteErrorEvent.h"
38#include "core/events/Event.h"
39#include "core/events/GenericEventQueue.h"
40#include "core/events/ScopedEventQueue.h"
41#include "core/frame/LocalDOMWindow.h"
42#include "core/frame/LocalFrame.h"
43#include "core/frame/UseCounter.h"
44#include "core/frame/csp/ContentSecurityPolicy.h"
45#include "core/html/HTMLCollection.h"
46#include "core/html/HTMLDialogElement.h"
47#include "core/html/HTMLFormControlsCollection.h"
48#include "core/html/HTMLImageElement.h"
49#include "core/html/HTMLInputElement.h"
50#include "core/html/HTMLObjectElement.h"
51#include "core/html/RadioNodeList.h"
52#include "core/html/forms/FormController.h"
53#include "core/inspector/ConsoleMessage.h"
54#include "core/loader/FrameLoader.h"
55#include "core/loader/FrameLoaderClient.h"
56#include "core/loader/MixedContentChecker.h"
57#include "core/rendering/RenderTextControl.h"
58#include "platform/UserGestureIndicator.h"
59#include "wtf/text/AtomicString.h"
60#include <limits>
61
62namespace blink {
63
64using namespace HTMLNames;
65
66HTMLFormElement::HTMLFormElement(Document& document)
67    : HTMLElement(formTag, document)
68#if !ENABLE(OILPAN)
69    , m_weakPtrFactory(this)
70#endif
71    , m_associatedElementsAreDirty(false)
72    , m_imageElementsAreDirty(false)
73    , m_hasElementsAssociatedByParser(false)
74    , m_didFinishParsingChildren(false)
75    , m_wasUserSubmitted(false)
76    , m_isSubmittingOrInUserJSSubmitEvent(false)
77    , m_shouldSubmit(false)
78    , m_isInResetFunction(false)
79    , m_wasDemoted(false)
80    , m_pendingAutocompleteEventsQueue(GenericEventQueue::create(this))
81{
82}
83
84PassRefPtrWillBeRawPtr<HTMLFormElement> HTMLFormElement::create(Document& document)
85{
86    UseCounter::count(document, UseCounter::FormElement);
87    return adoptRefWillBeNoop(new HTMLFormElement(document));
88}
89
90HTMLFormElement::~HTMLFormElement()
91{
92#if !ENABLE(OILPAN)
93    // With Oilpan, either removedFrom is called or the document and
94    // form controller are dead as well and there is no need to remove
95    // this form element from it.
96    document().formController().willDeleteForm(this);
97#endif
98}
99
100void HTMLFormElement::trace(Visitor* visitor)
101{
102#if ENABLE(OILPAN)
103    visitor->trace(m_pastNamesMap);
104    visitor->trace(m_radioButtonGroupScope);
105    visitor->trace(m_associatedElements);
106    visitor->trace(m_imageElements);
107    visitor->trace(m_pendingAutocompleteEventsQueue);
108#endif
109    HTMLElement::trace(visitor);
110}
111
112bool HTMLFormElement::rendererIsNeeded(const RenderStyle& style)
113{
114    if (!m_wasDemoted)
115        return HTMLElement::rendererIsNeeded(style);
116
117    ContainerNode* node = parentNode();
118    if (!node || !node->renderer())
119        return HTMLElement::rendererIsNeeded(style);
120    RenderObject* parentRenderer = node->renderer();
121    // FIXME: Shouldn't we also check for table caption (see |formIsTablePart| below).
122    // FIXME: This check is not correct for Shadow DOM.
123    bool parentIsTableElementPart = (parentRenderer->isTable() && isHTMLTableElement(*node))
124        || (parentRenderer->isTableRow() && isHTMLTableRowElement(*node))
125        || (parentRenderer->isTableSection() && node->hasTagName(tbodyTag))
126        || (parentRenderer->isRenderTableCol() && node->hasTagName(colTag))
127        || (parentRenderer->isTableCell() && isHTMLTableRowElement(*node));
128
129    if (!parentIsTableElementPart)
130        return true;
131
132    EDisplay display = style.display();
133    bool formIsTablePart = display == TABLE || display == INLINE_TABLE || display == TABLE_ROW_GROUP
134        || display == TABLE_HEADER_GROUP || display == TABLE_FOOTER_GROUP || display == TABLE_ROW
135        || display == TABLE_COLUMN_GROUP || display == TABLE_COLUMN || display == TABLE_CELL
136        || display == TABLE_CAPTION;
137
138    return formIsTablePart;
139}
140
141Node::InsertionNotificationRequest HTMLFormElement::insertedInto(ContainerNode* insertionPoint)
142{
143    if (insertionPoint->inDocument()) {
144        V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
145        if (activityLogger) {
146            Vector<String> argv;
147            argv.append("form");
148            argv.append(fastGetAttribute(methodAttr));
149            argv.append(fastGetAttribute(actionAttr));
150            activityLogger->logEvent("blinkAddElement", argv.size(), argv.data());
151        }
152    }
153    HTMLElement::insertedInto(insertionPoint);
154    if (insertionPoint->inDocument())
155        this->document().didAssociateFormControl(this);
156    return InsertionDone;
157}
158
159template<class T>
160void notifyFormRemovedFromTree(const T& elements, Node& root)
161{
162    size_t size = elements.size();
163    for (size_t i = 0; i < size; ++i)
164        elements[i]->formRemovedFromTree(root);
165    ASSERT(elements.size() == size);
166}
167
168void HTMLFormElement::removedFrom(ContainerNode* insertionPoint)
169{
170    // We don't need to take care of form association by 'form' content
171    // attribute becuse IdTargetObserver handles it.
172    if (m_hasElementsAssociatedByParser) {
173        Node& root = NodeTraversal::highestAncestorOrSelf(*this);
174        if (!m_associatedElementsAreDirty) {
175            FormAssociatedElement::List elements(associatedElements());
176            notifyFormRemovedFromTree(elements, root);
177        } else {
178            FormAssociatedElement::List elements;
179            collectAssociatedElements(NodeTraversal::highestAncestorOrSelf(*insertionPoint), elements);
180            notifyFormRemovedFromTree(elements, root);
181            collectAssociatedElements(root, elements);
182            notifyFormRemovedFromTree(elements, root);
183        }
184
185        if (!m_imageElementsAreDirty) {
186            WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> > images(imageElements());
187            notifyFormRemovedFromTree(images, root);
188        } else {
189            WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> > images;
190            collectImageElements(NodeTraversal::highestAncestorOrSelf(*insertionPoint), images);
191            notifyFormRemovedFromTree(images, root);
192            collectImageElements(root, images);
193            notifyFormRemovedFromTree(images, root);
194        }
195    }
196#if ENABLE(OILPAN)
197    document().formController().willDeleteForm(this);
198#endif
199    HTMLElement::removedFrom(insertionPoint);
200}
201
202void HTMLFormElement::handleLocalEvents(Event* event)
203{
204    Node* targetNode = event->target()->toNode();
205    if (event->eventPhase() != Event::CAPTURING_PHASE && targetNode && targetNode != this && (event->type() == EventTypeNames::submit || event->type() == EventTypeNames::reset)) {
206        event->stopPropagation();
207        return;
208    }
209    HTMLElement::handleLocalEvents(event);
210}
211
212unsigned HTMLFormElement::length() const
213{
214    const FormAssociatedElement::List& elements = associatedElements();
215    unsigned len = 0;
216    for (unsigned i = 0; i < elements.size(); ++i) {
217        if (elements[i]->isEnumeratable())
218            ++len;
219    }
220    return len;
221}
222
223HTMLElement* HTMLFormElement::item(unsigned index)
224{
225    return elements()->item(index);
226}
227
228void HTMLFormElement::submitImplicitly(Event* event, bool fromImplicitSubmissionTrigger)
229{
230    int submissionTriggerCount = 0;
231    bool seenDefaultButton = false;
232    const FormAssociatedElement::List& elements = associatedElements();
233    for (unsigned i = 0; i < elements.size(); ++i) {
234        FormAssociatedElement* formAssociatedElement = elements[i];
235        if (!formAssociatedElement->isFormControlElement())
236            continue;
237        HTMLFormControlElement* control = toHTMLFormControlElement(formAssociatedElement);
238        if (!seenDefaultButton && control->canBeSuccessfulSubmitButton()) {
239            if (fromImplicitSubmissionTrigger)
240                seenDefaultButton = true;
241            if (control->isSuccessfulSubmitButton()) {
242                control->dispatchSimulatedClick(event);
243                return;
244            } else if (fromImplicitSubmissionTrigger) {
245                // Default (submit) button is not activated; no implicit submission.
246                return;
247            }
248        } else if (control->canTriggerImplicitSubmission()) {
249            ++submissionTriggerCount;
250        }
251    }
252    if (fromImplicitSubmissionTrigger && submissionTriggerCount == 1)
253        prepareForSubmission(event);
254}
255
256// FIXME: Consolidate this and similar code in FormSubmission.cpp.
257static inline HTMLFormControlElement* submitElementFromEvent(const Event* event)
258{
259    for (Node* node = event->target()->toNode(); node; node = node->parentOrShadowHostNode()) {
260        if (node->isElementNode() && toElement(node)->isFormControlElement())
261            return toHTMLFormControlElement(node);
262    }
263    return 0;
264}
265
266bool HTMLFormElement::validateInteractively(Event* event)
267{
268    ASSERT(event);
269    if (!document().page() || noValidate())
270        return true;
271
272    HTMLFormControlElement* submitElement = submitElementFromEvent(event);
273    if (submitElement && submitElement->formNoValidate())
274        return true;
275
276    const FormAssociatedElement::List& elements = associatedElements();
277    for (unsigned i = 0; i < elements.size(); ++i) {
278        if (elements[i]->isFormControlElement())
279            toHTMLFormControlElement(elements[i])->hideVisibleValidationMessage();
280    }
281
282    WillBeHeapVector<RefPtrWillBeMember<FormAssociatedElement> > unhandledInvalidControls;
283    if (!checkInvalidControlsAndCollectUnhandled(&unhandledInvalidControls))
284        return true;
285    // Because the form has invalid controls, we abort the form submission and
286    // show a validation message on a focusable form control.
287
288    // Needs to update layout now because we'd like to call isFocusable(), which
289    // has !renderer()->needsLayout() assertion.
290    document().updateLayoutIgnorePendingStylesheets();
291
292    RefPtrWillBeRawPtr<HTMLFormElement> protector(this);
293    // Focus on the first focusable control and show a validation message.
294    for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) {
295        FormAssociatedElement* unhandledAssociatedElement = unhandledInvalidControls[i].get();
296        HTMLElement* unhandled = toHTMLElement(unhandledAssociatedElement);
297        if (unhandled->isFocusable() && unhandled->inDocument()) {
298            unhandled->scrollIntoViewIfNeeded(false);
299            unhandled->focus();
300            if (unhandled->isFormControlElement())
301                toHTMLFormControlElement(unhandled)->updateVisibleValidationMessage();
302            break;
303        }
304    }
305    // Warn about all of unfocusable controls.
306    if (document().frame()) {
307        for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) {
308            FormAssociatedElement* unhandledAssociatedElement = unhandledInvalidControls[i].get();
309            HTMLElement* unhandled = toHTMLElement(unhandledAssociatedElement);
310            if (unhandled->isFocusable() && unhandled->inDocument())
311                continue;
312            String message("An invalid form control with name='%name' is not focusable.");
313            message.replace("%name", unhandledAssociatedElement->name());
314            document().addConsoleMessage(ConsoleMessage::create(RenderingMessageSource, ErrorMessageLevel, message));
315        }
316    }
317    return false;
318}
319
320void HTMLFormElement::prepareForSubmission(Event* event)
321{
322    RefPtrWillBeRawPtr<HTMLFormElement> protector(this);
323    LocalFrame* frame = document().frame();
324    if (!frame || m_isSubmittingOrInUserJSSubmitEvent)
325        return;
326
327    // Interactive validation must be done before dispatching the submit event.
328    if (!validateInteractively(event))
329        return;
330
331    m_isSubmittingOrInUserJSSubmitEvent = true;
332    m_shouldSubmit = false;
333
334    frame->loader().client()->dispatchWillSendSubmitEvent(this);
335
336    if (dispatchEvent(Event::createCancelableBubble(EventTypeNames::submit)))
337        m_shouldSubmit = true;
338
339    m_isSubmittingOrInUserJSSubmitEvent = false;
340
341    if (m_shouldSubmit)
342        submit(event, true, true, NotSubmittedByJavaScript);
343}
344
345void HTMLFormElement::submit()
346{
347    submit(0, false, true, NotSubmittedByJavaScript);
348}
349
350void HTMLFormElement::submitFromJavaScript()
351{
352    submit(0, false, UserGestureIndicator::processingUserGesture(), SubmittedByJavaScript);
353}
354
355void HTMLFormElement::submitDialog(PassRefPtrWillBeRawPtr<FormSubmission> formSubmission)
356{
357    for (Node* node = this; node; node = node->parentOrShadowHostNode()) {
358        if (isHTMLDialogElement(*node)) {
359            toHTMLDialogElement(*node).closeDialog(formSubmission->result());
360            return;
361        }
362    }
363}
364
365void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool processingUserGesture, FormSubmissionTrigger formSubmissionTrigger)
366{
367    FrameView* view = document().view();
368    LocalFrame* frame = document().frame();
369    if (!view || !frame || !frame->page())
370        return;
371
372    if (m_isSubmittingOrInUserJSSubmitEvent) {
373        m_shouldSubmit = true;
374        return;
375    }
376
377    m_isSubmittingOrInUserJSSubmitEvent = true;
378    m_wasUserSubmitted = processingUserGesture;
379
380    RefPtrWillBeRawPtr<HTMLFormControlElement> firstSuccessfulSubmitButton = nullptr;
381    bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button?
382
383    const FormAssociatedElement::List& elements = associatedElements();
384    for (unsigned i = 0; i < elements.size(); ++i) {
385        FormAssociatedElement* associatedElement = elements[i];
386        if (!associatedElement->isFormControlElement())
387            continue;
388        if (needButtonActivation) {
389            HTMLFormControlElement* control = toHTMLFormControlElement(associatedElement);
390            if (control->isActivatedSubmit())
391                needButtonActivation = false;
392            else if (firstSuccessfulSubmitButton == 0 && control->isSuccessfulSubmitButton())
393                firstSuccessfulSubmitButton = control;
394        }
395    }
396
397    if (needButtonActivation && firstSuccessfulSubmitButton)
398        firstSuccessfulSubmitButton->setActivatedSubmit(true);
399
400    RefPtrWillBeRawPtr<FormSubmission> formSubmission = FormSubmission::create(this, m_attributes, event, formSubmissionTrigger);
401    EventQueueScope scopeForDialogClose; // Delay dispatching 'close' to dialog until done submitting.
402    if (formSubmission->method() == FormSubmission::DialogMethod)
403        submitDialog(formSubmission.release());
404    else
405        scheduleFormSubmission(formSubmission.release());
406
407    if (needButtonActivation && firstSuccessfulSubmitButton)
408        firstSuccessfulSubmitButton->setActivatedSubmit(false);
409
410    m_shouldSubmit = false;
411    m_isSubmittingOrInUserJSSubmitEvent = false;
412}
413
414void HTMLFormElement::scheduleFormSubmission(PassRefPtrWillBeRawPtr<FormSubmission> submission)
415{
416    ASSERT(submission->method() == FormSubmission::PostMethod || submission->method() == FormSubmission::GetMethod);
417    ASSERT(submission->data());
418    ASSERT(submission->state());
419    if (submission->action().isEmpty())
420        return;
421    if (document().isSandboxed(SandboxForms)) {
422        // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
423        document().addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, "Blocked form submission to '" + submission->action().elidedString() + "' because the form's frame is sandboxed and the 'allow-forms' permission is not set."));
424        return;
425    }
426
427    if (protocolIsJavaScript(submission->action())) {
428        if (!document().contentSecurityPolicy()->allowFormAction(submission->action()))
429            return;
430        document().frame()->script().executeScriptIfJavaScriptURL(submission->action());
431        return;
432    }
433
434    LocalFrame* targetFrame = document().frame()->loader().findFrameForNavigation(submission->target(), submission->state()->sourceDocument());
435    if (!targetFrame) {
436        if (!LocalDOMWindow::allowPopUp(*document().frame()) && !UserGestureIndicator::processingUserGesture())
437            return;
438        targetFrame = document().frame();
439    } else {
440        submission->clearTarget();
441    }
442    if (!targetFrame->page())
443        return;
444
445    if (MixedContentChecker::isMixedContent(document().securityOrigin(), submission->action())) {
446        UseCounter::count(document(), UseCounter::MixedContentFormsSubmitted);
447        if (!document().frame()->loader().mixedContentChecker()->canSubmitToInsecureForm(document().securityOrigin(), submission->action()))
448            return;
449    } else {
450        UseCounter::count(document(), UseCounter::FormsSubmitted);
451    }
452
453    submission->setReferrer(Referrer(document().outgoingReferrer(), document().referrerPolicy()));
454    submission->setOrigin(document().outgoingOrigin());
455
456    targetFrame->navigationScheduler().scheduleFormSubmission(submission);
457}
458
459void HTMLFormElement::reset()
460{
461    LocalFrame* frame = document().frame();
462    if (m_isInResetFunction || !frame)
463        return;
464
465    m_isInResetFunction = true;
466
467    if (!dispatchEvent(Event::createCancelableBubble(EventTypeNames::reset))) {
468        m_isInResetFunction = false;
469        return;
470    }
471
472    const FormAssociatedElement::List& elements = associatedElements();
473    for (unsigned i = 0; i < elements.size(); ++i) {
474        if (elements[i]->isFormControlElement())
475            toHTMLFormControlElement(elements[i])->reset();
476    }
477
478    m_isInResetFunction = false;
479}
480
481void HTMLFormElement::requestAutocomplete()
482{
483    String errorMessage;
484
485    if (!document().frame())
486        errorMessage = "requestAutocomplete: form is not owned by a displayed document.";
487    else if (!shouldAutocomplete())
488        errorMessage = "requestAutocomplete: form autocomplete attribute is set to off.";
489    else if (!UserGestureIndicator::processingUserGesture())
490        errorMessage = "requestAutocomplete: must be called in response to a user gesture.";
491
492    if (!errorMessage.isEmpty()) {
493        document().addConsoleMessage(ConsoleMessage::create(RenderingMessageSource, LogMessageLevel, errorMessage));
494        finishRequestAutocomplete(AutocompleteResultErrorDisabled);
495    } else {
496        document().frame()->loader().client()->didRequestAutocomplete(this);
497    }
498}
499
500void HTMLFormElement::finishRequestAutocomplete(AutocompleteResult result)
501{
502    RefPtrWillBeRawPtr<Event> event = nullptr;
503    if (result == AutocompleteResultSuccess)
504        event = Event::createBubble(EventTypeNames::autocomplete);
505    else if (result == AutocompleteResultErrorDisabled)
506        event = AutocompleteErrorEvent::create("disabled");
507    else if (result == AutocompleteResultErrorCancel)
508        event = AutocompleteErrorEvent::create("cancel");
509    else if (result == AutocompleteResultErrorInvalid)
510        event = AutocompleteErrorEvent::create("invalid");
511    else
512        ASSERT_NOT_REACHED();
513
514    event->setTarget(this);
515    m_pendingAutocompleteEventsQueue->enqueueEvent(event.release());
516}
517
518void HTMLFormElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
519{
520    if (name == actionAttr) {
521        m_attributes.parseAction(value);
522        // If the new action attribute is pointing to insecure "action" location from a secure page
523        // it is marked as "passive" mixed content.
524        KURL actionURL = document().completeURL(m_attributes.action().isEmpty() ? document().url().string() : m_attributes.action());
525        if (document().frame() && MixedContentChecker::isMixedContent(document().securityOrigin(), actionURL))
526            document().frame()->loader().mixedContentChecker()->canSubmitToInsecureForm(document().securityOrigin(), actionURL);
527    } else if (name == targetAttr)
528        m_attributes.setTarget(value);
529    else if (name == methodAttr)
530        m_attributes.updateMethodType(value);
531    else if (name == enctypeAttr)
532        m_attributes.updateEncodingType(value);
533    else if (name == accept_charsetAttr)
534        m_attributes.setAcceptCharset(value);
535    else if (name == onautocompleteAttr)
536        setAttributeEventListener(EventTypeNames::autocomplete, createAttributeEventListener(this, name, value, eventParameterName()));
537    else if (name == onautocompleteerrorAttr)
538        setAttributeEventListener(EventTypeNames::autocompleteerror, createAttributeEventListener(this, name, value, eventParameterName()));
539    else
540        HTMLElement::parseAttribute(name, value);
541}
542
543void HTMLFormElement::attributeWillChange(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue)
544{
545    if (name == actionAttr && inDocument()) {
546        V8DOMActivityLogger* activityLogger = V8DOMActivityLogger::currentActivityLoggerIfIsolatedWorld();
547        if (activityLogger) {
548            Vector<String> argv;
549            argv.append("form");
550            argv.append(actionAttr.toString());
551            argv.append(oldValue);
552            argv.append(newValue);
553            activityLogger->logEvent("blinkSetAttribute", argv.size(), argv.data());
554        }
555    }
556    HTMLElement::attributeWillChange(name, oldValue, newValue);
557}
558
559void HTMLFormElement::associate(FormAssociatedElement& e)
560{
561    m_associatedElementsAreDirty = true;
562    m_associatedElements.clear();
563}
564
565void HTMLFormElement::disassociate(FormAssociatedElement& e)
566{
567    m_associatedElementsAreDirty = true;
568    m_associatedElements.clear();
569    removeFromPastNamesMap(toHTMLElement(e));
570}
571
572bool HTMLFormElement::isURLAttribute(const Attribute& attribute) const
573{
574    return attribute.name() == actionAttr || HTMLElement::isURLAttribute(attribute);
575}
576
577bool HTMLFormElement::hasLegalLinkAttribute(const QualifiedName& name) const
578{
579    return name == actionAttr || HTMLElement::hasLegalLinkAttribute(name);
580}
581
582void HTMLFormElement::associate(HTMLImageElement& e)
583{
584    m_imageElementsAreDirty = true;
585    m_imageElements.clear();
586}
587
588void HTMLFormElement::disassociate(HTMLImageElement& e)
589{
590    m_imageElementsAreDirty = true;
591    m_imageElements.clear();
592    removeFromPastNamesMap(e);
593}
594
595#if !ENABLE(OILPAN)
596WeakPtr<HTMLFormElement> HTMLFormElement::createWeakPtr()
597{
598    return m_weakPtrFactory.createWeakPtr();
599}
600#endif
601
602void HTMLFormElement::didAssociateByParser()
603{
604    if (!m_didFinishParsingChildren)
605        return;
606    m_hasElementsAssociatedByParser = true;
607    UseCounter::count(document(), UseCounter::FormAssociationByParser);
608}
609
610PassRefPtrWillBeRawPtr<HTMLFormControlsCollection> HTMLFormElement::elements()
611{
612    return ensureCachedCollection<HTMLFormControlsCollection>(FormControls);
613}
614
615void HTMLFormElement::collectAssociatedElements(Node& root, FormAssociatedElement::List& elements) const
616{
617    elements.clear();
618    for (HTMLElement* element = Traversal<HTMLElement>::firstWithin(root); element; element = Traversal<HTMLElement>::next(*element)) {
619        FormAssociatedElement* associatedElement = 0;
620        if (element->isFormControlElement())
621            associatedElement = toHTMLFormControlElement(element);
622        else if (isHTMLObjectElement(*element))
623            associatedElement = toHTMLObjectElement(element);
624        else
625            continue;
626        if (associatedElement->form()== this)
627            elements.append(associatedElement);
628    }
629}
630
631// This function should be const conceptually. However we update some fields
632// because of lazy evaluation.
633const FormAssociatedElement::List& HTMLFormElement::associatedElements() const
634{
635    if (!m_associatedElementsAreDirty)
636        return m_associatedElements;
637    HTMLFormElement* mutableThis = const_cast<HTMLFormElement*>(this);
638    Node* scope = mutableThis;
639    if (m_hasElementsAssociatedByParser)
640        scope = &NodeTraversal::highestAncestorOrSelf(*mutableThis);
641    if (inDocument() && treeScope().idTargetObserverRegistry().hasObservers(fastGetAttribute(idAttr)))
642        scope = &treeScope().rootNode();
643    ASSERT(scope);
644    collectAssociatedElements(*scope, mutableThis->m_associatedElements);
645    mutableThis->m_associatedElementsAreDirty = false;
646    return m_associatedElements;
647}
648
649void HTMLFormElement::collectImageElements(Node& root, WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> >& elements)
650{
651    elements.clear();
652    for (HTMLImageElement* image = Traversal<HTMLImageElement>::firstWithin(root); image; image = Traversal<HTMLImageElement>::next(*image)) {
653        if (image->formOwner() == this)
654            elements.append(image);
655    }
656}
657
658const WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> >& HTMLFormElement::imageElements()
659{
660    if (!m_imageElementsAreDirty)
661        return m_imageElements;
662    collectImageElements(m_hasElementsAssociatedByParser ? NodeTraversal::highestAncestorOrSelf(*this) : *this, m_imageElements);
663    m_imageElementsAreDirty = false;
664    return m_imageElements;
665}
666
667String HTMLFormElement::name() const
668{
669    return getNameAttribute();
670}
671
672bool HTMLFormElement::noValidate() const
673{
674    return fastHasAttribute(novalidateAttr);
675}
676
677// FIXME: This function should be removed because it does not do the same thing as the
678// JavaScript binding for action, which treats action as a URL attribute. Last time I
679// (Darin Adler) removed this, someone added it back, so I am leaving it in for now.
680const AtomicString& HTMLFormElement::action() const
681{
682    return getAttribute(actionAttr);
683}
684
685void HTMLFormElement::setEnctype(const AtomicString& value)
686{
687    setAttribute(enctypeAttr, value);
688}
689
690String HTMLFormElement::method() const
691{
692    return FormSubmission::Attributes::methodString(m_attributes.method());
693}
694
695void HTMLFormElement::setMethod(const AtomicString& value)
696{
697    setAttribute(methodAttr, value);
698}
699
700bool HTMLFormElement::wasUserSubmitted() const
701{
702    return m_wasUserSubmitted;
703}
704
705HTMLFormControlElement* HTMLFormElement::defaultButton() const
706{
707    const FormAssociatedElement::List& elements = associatedElements();
708    for (unsigned i = 0; i < elements.size(); ++i) {
709        if (!elements[i]->isFormControlElement())
710            continue;
711        HTMLFormControlElement* control = toHTMLFormControlElement(elements[i]);
712        if (control->isSuccessfulSubmitButton())
713            return control;
714    }
715
716    return 0;
717}
718
719bool HTMLFormElement::checkValidity()
720{
721    return !checkInvalidControlsAndCollectUnhandled(0);
722}
723
724bool HTMLFormElement::checkInvalidControlsAndCollectUnhandled(WillBeHeapVector<RefPtrWillBeMember<FormAssociatedElement> >* unhandledInvalidControls)
725{
726    RefPtrWillBeRawPtr<HTMLFormElement> protector(this);
727    // Copy associatedElements because event handlers called from
728    // HTMLFormControlElement::checkValidity() might change associatedElements.
729    const FormAssociatedElement::List& associatedElements = this->associatedElements();
730    WillBeHeapVector<RefPtrWillBeMember<FormAssociatedElement> > elements;
731    elements.reserveCapacity(associatedElements.size());
732    for (unsigned i = 0; i < associatedElements.size(); ++i)
733        elements.append(associatedElements[i]);
734    bool hasInvalidControls = false;
735    for (unsigned i = 0; i < elements.size(); ++i) {
736        if (elements[i]->form() == this && elements[i]->isFormControlElement()) {
737            HTMLFormControlElement* control = toHTMLFormControlElement(elements[i].get());
738            if (!control->checkValidity(unhandledInvalidControls) && control->formOwner() == this)
739                hasInvalidControls = true;
740        }
741    }
742    return hasInvalidControls;
743}
744
745Element* HTMLFormElement::elementFromPastNamesMap(const AtomicString& pastName)
746{
747    if (pastName.isEmpty() || !m_pastNamesMap)
748        return 0;
749    Element* element = m_pastNamesMap->get(pastName);
750#if ENABLE(ASSERT)
751    if (!element)
752        return 0;
753    ASSERT_WITH_SECURITY_IMPLICATION(toHTMLElement(element)->formOwner() == this);
754    if (isHTMLImageElement(*element)) {
755        ASSERT_WITH_SECURITY_IMPLICATION(imageElements().find(element) != kNotFound);
756    } else if (isHTMLObjectElement(*element)) {
757        ASSERT_WITH_SECURITY_IMPLICATION(associatedElements().find(toHTMLObjectElement(element)) != kNotFound);
758    } else {
759        ASSERT_WITH_SECURITY_IMPLICATION(associatedElements().find(toHTMLFormControlElement(element)) != kNotFound);
760    }
761#endif
762    return element;
763}
764
765void HTMLFormElement::addToPastNamesMap(Element* element, const AtomicString& pastName)
766{
767    if (pastName.isEmpty())
768        return;
769    if (!m_pastNamesMap)
770        m_pastNamesMap = adoptPtrWillBeNoop(new PastNamesMap);
771    m_pastNamesMap->set(pastName, element);
772}
773
774void HTMLFormElement::removeFromPastNamesMap(HTMLElement& element)
775{
776    if (!m_pastNamesMap)
777        return;
778    PastNamesMap::iterator end = m_pastNamesMap->end();
779    for (PastNamesMap::iterator it = m_pastNamesMap->begin(); it != end; ++it) {
780        if (it->value == &element) {
781            it->value = nullptr;
782            // Keep looping. Single element can have multiple names.
783        }
784    }
785}
786
787void HTMLFormElement::getNamedElements(const AtomicString& name, WillBeHeapVector<RefPtrWillBeMember<Element> >& namedItems)
788{
789    // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem
790    elements()->namedItems(name, namedItems);
791
792    Element* elementFromPast = elementFromPastNamesMap(name);
793    if (namedItems.size() && namedItems.first() != elementFromPast) {
794        addToPastNamesMap(namedItems.first().get(), name);
795    } else if (elementFromPast && namedItems.isEmpty()) {
796        namedItems.append(elementFromPast);
797        UseCounter::count(document(), UseCounter::FormNameAccessForPastNamesMap);
798    }
799}
800
801bool HTMLFormElement::shouldAutocomplete() const
802{
803    return !equalIgnoringCase(fastGetAttribute(autocompleteAttr), "off");
804}
805
806void HTMLFormElement::finishParsingChildren()
807{
808    HTMLElement::finishParsingChildren();
809    document().formController().restoreControlStateIn(*this);
810    m_didFinishParsingChildren = true;
811}
812
813void HTMLFormElement::copyNonAttributePropertiesFromElement(const Element& source)
814{
815    m_wasDemoted = static_cast<const HTMLFormElement&>(source).m_wasDemoted;
816    HTMLElement::copyNonAttributePropertiesFromElement(source);
817}
818
819void HTMLFormElement::anonymousNamedGetter(const AtomicString& name, RefPtrWillBeRawPtr<RadioNodeList>& returnValue0, RefPtrWillBeRawPtr<Element>& returnValue1)
820{
821    // Call getNamedElements twice, first time check if it has a value
822    // and let HTMLFormElement update its cache.
823    // See issue: 867404
824    {
825        WillBeHeapVector<RefPtrWillBeMember<Element> > elements;
826        getNamedElements(name, elements);
827        if (elements.isEmpty())
828            return;
829    }
830
831    // Second call may return different results from the first call,
832    // but if the first the size cannot be zero.
833    WillBeHeapVector<RefPtrWillBeMember<Element> > elements;
834    getNamedElements(name, elements);
835    ASSERT(!elements.isEmpty());
836
837    if (elements.size() == 1) {
838        returnValue1 = elements.at(0);
839        return;
840    }
841
842    bool onlyMatchImg = !elements.isEmpty() && isHTMLImageElement(*elements.first());
843    returnValue0 = radioNodeList(name, onlyMatchImg);
844}
845
846void HTMLFormElement::setDemoted(bool demoted)
847{
848    if (demoted)
849        UseCounter::count(document(), UseCounter::DemotedFormElement);
850    m_wasDemoted = demoted;
851}
852
853} // namespace
854