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