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