1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2001 Dirk Mueller (mueller (at) kde.org) 5 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 6 * (C) 2006 Alexey Proskuryakov (ap (at) 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 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 82 PassRefPtrWillBeRawPtr<HTMLFormElement> HTMLFormElement::create(Document& document) 83 { 84 UseCounter::count(document, UseCounter::FormElement); 85 return adoptRefWillBeNoop(new HTMLFormElement(document)); 86 } 87 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 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 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 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> 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 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 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 200 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 211 Element* HTMLFormElement::item(unsigned index) 212 { 213 return elements()->item(index); 214 } 215 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. 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 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 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 333 void HTMLFormElement::submit() 334 { 335 submit(0, false, true, NotSubmittedByJavaScript); 336 } 337 338 void HTMLFormElement::submitFromJavaScript() 339 { 340 submit(0, false, UserGestureIndicator::processingUserGesture(), SubmittedByJavaScript); 341 } 342 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 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 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 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 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 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 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 531 void HTMLFormElement::associate(FormAssociatedElement& e) 532 { 533 m_associatedElementsAreDirty = true; 534 m_associatedElements.clear(); 535 } 536 537 void HTMLFormElement::disassociate(FormAssociatedElement& e) 538 { 539 m_associatedElementsAreDirty = true; 540 m_associatedElements.clear(); 541 removeFromPastNamesMap(toHTMLElement(e)); 542 } 543 544 bool HTMLFormElement::isURLAttribute(const Attribute& attribute) const 545 { 546 return attribute.name() == actionAttr || HTMLElement::isURLAttribute(attribute); 547 } 548 549 bool HTMLFormElement::hasLegalLinkAttribute(const QualifiedName& name) const 550 { 551 return name == actionAttr || HTMLElement::hasLegalLinkAttribute(name); 552 } 553 554 void HTMLFormElement::associate(HTMLImageElement& e) 555 { 556 m_imageElementsAreDirty = true; 557 m_imageElements.clear(); 558 } 559 560 void HTMLFormElement::disassociate(HTMLImageElement& e) 561 { 562 m_imageElementsAreDirty = true; 563 m_imageElements.clear(); 564 removeFromPastNamesMap(e); 565 } 566 567 #if !ENABLE(OILPAN) 568 WeakPtr<HTMLFormElement> HTMLFormElement::createWeakPtr() 569 { 570 return m_weakPtrFactory.createWeakPtr(); 571 } 572 #endif 573 574 void HTMLFormElement::didAssociateByParser() 575 { 576 if (!m_didFinishParsingChildren) 577 return; 578 m_hasElementsAssociatedByParser = true; 579 UseCounter::count(document(), UseCounter::FormAssociationByParser); 580 } 581 582 PassRefPtrWillBeRawPtr<HTMLFormControlsCollection> HTMLFormElement::elements() 583 { 584 return toHTMLFormControlsCollection(ensureCachedHTMLCollection(FormControls).get()); 585 } 586 587 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. 605 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 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 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 639 String HTMLFormElement::name() const 640 { 641 return getNameAttribute(); 642 } 643 644 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. 652 const AtomicString& HTMLFormElement::action() const 653 { 654 return getAttribute(actionAttr); 655 } 656 657 void HTMLFormElement::setEnctype(const AtomicString& value) 658 { 659 setAttribute(enctypeAttr, value); 660 } 661 662 String HTMLFormElement::method() const 663 { 664 return FormSubmission::Attributes::methodString(m_attributes.method()); 665 } 666 667 void HTMLFormElement::setMethod(const AtomicString& value) 668 { 669 setAttribute(methodAttr, value); 670 } 671 672 bool HTMLFormElement::wasUserSubmitted() const 673 { 674 return m_wasUserSubmitted; 675 } 676 677 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 691 bool HTMLFormElement::checkValidity() 692 { 693 return !checkInvalidControlsAndCollectUnhandled(0); 694 } 695 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 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 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 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 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 773 bool HTMLFormElement::shouldAutocomplete() const 774 { 775 return !equalIgnoringCase(fastGetAttribute(autocompleteAttr), "off"); 776 } 777 778 void HTMLFormElement::finishParsingChildren() 779 { 780 HTMLElement::finishParsingChildren(); 781 document().formController().restoreControlStateIn(*this); 782 m_didFinishParsingChildren = true; 783 } 784 785 void HTMLFormElement::copyNonAttributePropertiesFromElement(const Element& source) 786 { 787 m_wasDemoted = static_cast<const HTMLFormElement&>(source).m_wasDemoted; 788 HTMLElement::copyNonAttributePropertiesFromElement(source); 789 } 790 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 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