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 "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 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 84 PassRefPtrWillBeRawPtr<HTMLFormElement> HTMLFormElement::create(Document& document) 85 { 86 UseCounter::count(document, UseCounter::FormElement); 87 return adoptRefWillBeNoop(new HTMLFormElement(document)); 88 } 89 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 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 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 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> 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 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 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 212 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 223 HTMLElement* HTMLFormElement::item(unsigned index) 224 { 225 return elements()->item(index); 226 } 227 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. 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 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 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 345 void HTMLFormElement::submit() 346 { 347 submit(0, false, true, NotSubmittedByJavaScript); 348 } 349 350 void HTMLFormElement::submitFromJavaScript() 351 { 352 submit(0, false, UserGestureIndicator::processingUserGesture(), SubmittedByJavaScript); 353 } 354 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 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 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 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 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 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 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 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 559 void HTMLFormElement::associate(FormAssociatedElement& e) 560 { 561 m_associatedElementsAreDirty = true; 562 m_associatedElements.clear(); 563 } 564 565 void HTMLFormElement::disassociate(FormAssociatedElement& e) 566 { 567 m_associatedElementsAreDirty = true; 568 m_associatedElements.clear(); 569 removeFromPastNamesMap(toHTMLElement(e)); 570 } 571 572 bool HTMLFormElement::isURLAttribute(const Attribute& attribute) const 573 { 574 return attribute.name() == actionAttr || HTMLElement::isURLAttribute(attribute); 575 } 576 577 bool HTMLFormElement::hasLegalLinkAttribute(const QualifiedName& name) const 578 { 579 return name == actionAttr || HTMLElement::hasLegalLinkAttribute(name); 580 } 581 582 void HTMLFormElement::associate(HTMLImageElement& e) 583 { 584 m_imageElementsAreDirty = true; 585 m_imageElements.clear(); 586 } 587 588 void HTMLFormElement::disassociate(HTMLImageElement& e) 589 { 590 m_imageElementsAreDirty = true; 591 m_imageElements.clear(); 592 removeFromPastNamesMap(e); 593 } 594 595 #if !ENABLE(OILPAN) 596 WeakPtr<HTMLFormElement> HTMLFormElement::createWeakPtr() 597 { 598 return m_weakPtrFactory.createWeakPtr(); 599 } 600 #endif 601 602 void HTMLFormElement::didAssociateByParser() 603 { 604 if (!m_didFinishParsingChildren) 605 return; 606 m_hasElementsAssociatedByParser = true; 607 UseCounter::count(document(), UseCounter::FormAssociationByParser); 608 } 609 610 PassRefPtrWillBeRawPtr<HTMLFormControlsCollection> HTMLFormElement::elements() 611 { 612 return ensureCachedCollection<HTMLFormControlsCollection>(FormControls); 613 } 614 615 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. 633 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 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 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 667 String HTMLFormElement::name() const 668 { 669 return getNameAttribute(); 670 } 671 672 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. 680 const AtomicString& HTMLFormElement::action() const 681 { 682 return getAttribute(actionAttr); 683 } 684 685 void HTMLFormElement::setEnctype(const AtomicString& value) 686 { 687 setAttribute(enctypeAttr, value); 688 } 689 690 String HTMLFormElement::method() const 691 { 692 return FormSubmission::Attributes::methodString(m_attributes.method()); 693 } 694 695 void HTMLFormElement::setMethod(const AtomicString& value) 696 { 697 setAttribute(methodAttr, value); 698 } 699 700 bool HTMLFormElement::wasUserSubmitted() const 701 { 702 return m_wasUserSubmitted; 703 } 704 705 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 719 bool HTMLFormElement::checkValidity() 720 { 721 return !checkInvalidControlsAndCollectUnhandled(0); 722 } 723 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 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 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 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 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 801 bool HTMLFormElement::shouldAutocomplete() const 802 { 803 return !equalIgnoringCase(fastGetAttribute(autocompleteAttr), "off"); 804 } 805 806 void HTMLFormElement::finishParsingChildren() 807 { 808 HTMLElement::finishParsingChildren(); 809 document().formController().restoreControlStateIn(*this); 810 m_didFinishParsingChildren = true; 811 } 812 813 void HTMLFormElement::copyNonAttributePropertiesFromElement(const Element& source) 814 { 815 m_wasDemoted = static_cast<const HTMLFormElement&>(source).m_wasDemoted; 816 HTMLElement::copyNonAttributePropertiesFromElement(source); 817 } 818 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 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