• 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 "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 
62 namespace blink {
63 
64 using namespace HTMLNames;
65 
HTMLFormElement(Document & document)66 HTMLFormElement::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 
create(Document & document)84 PassRefPtrWillBeRawPtr<HTMLFormElement> HTMLFormElement::create(Document& document)
85 {
86     UseCounter::count(document, UseCounter::FormElement);
87     return adoptRefWillBeNoop(new HTMLFormElement(document));
88 }
89 
~HTMLFormElement()90 HTMLFormElement::~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 
trace(Visitor * visitor)100 void 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 
rendererIsNeeded(const RenderStyle & style)112 bool 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 
insertedInto(ContainerNode * insertionPoint)141 Node::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 
159 template<class T>
notifyFormRemovedFromTree(const T & elements,Node & root)160 void 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 
removedFrom(ContainerNode * insertionPoint)168 void 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 
handleLocalEvents(Event * event)202 void 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 
length() const212 unsigned 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 
item(unsigned index)223 HTMLElement* HTMLFormElement::item(unsigned index)
224 {
225     return elements()->item(index);
226 }
227 
submitImplicitly(Event * event,bool fromImplicitSubmissionTrigger)228 void 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.
submitElementFromEvent(const Event * event)257 static 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 
validateInteractively(Event * event)266 bool 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 
prepareForSubmission(Event * event)320 void 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 
submit()345 void HTMLFormElement::submit()
346 {
347     submit(0, false, true, NotSubmittedByJavaScript);
348 }
349 
submitFromJavaScript()350 void HTMLFormElement::submitFromJavaScript()
351 {
352     submit(0, false, UserGestureIndicator::processingUserGesture(), SubmittedByJavaScript);
353 }
354 
submitDialog(PassRefPtrWillBeRawPtr<FormSubmission> formSubmission)355 void 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 
submit(Event * event,bool activateSubmitButton,bool processingUserGesture,FormSubmissionTrigger formSubmissionTrigger)365 void 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 
scheduleFormSubmission(PassRefPtrWillBeRawPtr<FormSubmission> submission)414 void 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 
reset()459 void 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 
requestAutocomplete()481 void 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 
finishRequestAutocomplete(AutocompleteResult result)500 void 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 
parseAttribute(const QualifiedName & name,const AtomicString & value)518 void 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 
attributeWillChange(const QualifiedName & name,const AtomicString & oldValue,const AtomicString & newValue)543 void 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 
associate(FormAssociatedElement & e)559 void HTMLFormElement::associate(FormAssociatedElement& e)
560 {
561     m_associatedElementsAreDirty = true;
562     m_associatedElements.clear();
563 }
564 
disassociate(FormAssociatedElement & e)565 void HTMLFormElement::disassociate(FormAssociatedElement& e)
566 {
567     m_associatedElementsAreDirty = true;
568     m_associatedElements.clear();
569     removeFromPastNamesMap(toHTMLElement(e));
570 }
571 
isURLAttribute(const Attribute & attribute) const572 bool HTMLFormElement::isURLAttribute(const Attribute& attribute) const
573 {
574     return attribute.name() == actionAttr || HTMLElement::isURLAttribute(attribute);
575 }
576 
hasLegalLinkAttribute(const QualifiedName & name) const577 bool HTMLFormElement::hasLegalLinkAttribute(const QualifiedName& name) const
578 {
579     return name == actionAttr || HTMLElement::hasLegalLinkAttribute(name);
580 }
581 
associate(HTMLImageElement & e)582 void HTMLFormElement::associate(HTMLImageElement& e)
583 {
584     m_imageElementsAreDirty = true;
585     m_imageElements.clear();
586 }
587 
disassociate(HTMLImageElement & e)588 void HTMLFormElement::disassociate(HTMLImageElement& e)
589 {
590     m_imageElementsAreDirty = true;
591     m_imageElements.clear();
592     removeFromPastNamesMap(e);
593 }
594 
595 #if !ENABLE(OILPAN)
createWeakPtr()596 WeakPtr<HTMLFormElement> HTMLFormElement::createWeakPtr()
597 {
598     return m_weakPtrFactory.createWeakPtr();
599 }
600 #endif
601 
didAssociateByParser()602 void HTMLFormElement::didAssociateByParser()
603 {
604     if (!m_didFinishParsingChildren)
605         return;
606     m_hasElementsAssociatedByParser = true;
607     UseCounter::count(document(), UseCounter::FormAssociationByParser);
608 }
609 
elements()610 PassRefPtrWillBeRawPtr<HTMLFormControlsCollection> HTMLFormElement::elements()
611 {
612     return ensureCachedCollection<HTMLFormControlsCollection>(FormControls);
613 }
614 
collectAssociatedElements(Node & root,FormAssociatedElement::List & elements) const615 void 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.
associatedElements() const633 const 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 
collectImageElements(Node & root,WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement>> & elements)649 void 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 
imageElements()658 const 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 
name() const667 String HTMLFormElement::name() const
668 {
669     return getNameAttribute();
670 }
671 
noValidate() const672 bool 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.
action() const680 const AtomicString& HTMLFormElement::action() const
681 {
682     return getAttribute(actionAttr);
683 }
684 
setEnctype(const AtomicString & value)685 void HTMLFormElement::setEnctype(const AtomicString& value)
686 {
687     setAttribute(enctypeAttr, value);
688 }
689 
method() const690 String HTMLFormElement::method() const
691 {
692     return FormSubmission::Attributes::methodString(m_attributes.method());
693 }
694 
setMethod(const AtomicString & value)695 void HTMLFormElement::setMethod(const AtomicString& value)
696 {
697     setAttribute(methodAttr, value);
698 }
699 
wasUserSubmitted() const700 bool HTMLFormElement::wasUserSubmitted() const
701 {
702     return m_wasUserSubmitted;
703 }
704 
defaultButton() const705 HTMLFormControlElement* 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 
checkValidity()719 bool HTMLFormElement::checkValidity()
720 {
721     return !checkInvalidControlsAndCollectUnhandled(0);
722 }
723 
checkInvalidControlsAndCollectUnhandled(WillBeHeapVector<RefPtrWillBeMember<FormAssociatedElement>> * unhandledInvalidControls)724 bool 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 
elementFromPastNamesMap(const AtomicString & pastName)745 Element* 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 
addToPastNamesMap(Element * element,const AtomicString & pastName)765 void 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 
removeFromPastNamesMap(HTMLElement & element)774 void 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 
getNamedElements(const AtomicString & name,WillBeHeapVector<RefPtrWillBeMember<Element>> & namedItems)787 void 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 
shouldAutocomplete() const801 bool HTMLFormElement::shouldAutocomplete() const
802 {
803     return !equalIgnoringCase(fastGetAttribute(autocompleteAttr), "off");
804 }
805 
finishParsingChildren()806 void HTMLFormElement::finishParsingChildren()
807 {
808     HTMLElement::finishParsingChildren();
809     document().formController().restoreControlStateIn(*this);
810     m_didFinishParsingChildren = true;
811 }
812 
copyNonAttributePropertiesFromElement(const Element & source)813 void HTMLFormElement::copyNonAttributePropertiesFromElement(const Element& source)
814 {
815     m_wasDemoted = static_cast<const HTMLFormElement&>(source).m_wasDemoted;
816     HTMLElement::copyNonAttributePropertiesFromElement(source);
817 }
818 
anonymousNamedGetter(const AtomicString & name,RefPtrWillBeRawPtr<RadioNodeList> & returnValue0,RefPtrWillBeRawPtr<Element> & returnValue1)819 void 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 
setDemoted(bool demoted)846 void HTMLFormElement::setDemoted(bool demoted)
847 {
848     if (demoted)
849         UseCounter::count(document(), UseCounter::DemotedFormElement);
850     m_wasDemoted = demoted;
851 }
852 
853 } // namespace
854