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