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